1.2 线性表的链式表示

/* 
    链表
 */

#include<iostream>

using namespace std;

//单链表结点类型描述
typedef struct Lnode{
    ElemType data;
    struct Lnode *next;
}LNode, *LinkList;

//双链表
typedef struct DNode{
    ElemType data;
    int freq;
    struct DNode *prior, *next;
}DNode, *DLinkList;

/* 1. 递归地删除不带头结点的单链表L中所有值为x的结点 */
void Dele_X(LinkList &L, ElemType x){
    LNode *p;
    if (L == NULL) return;
    if (L->data == x){
        p = L;
        L = L->next;
        free(p);
        Dele_X(L, x);
    }
    else
        Dele_X(L->next, x);
}

/* 2. 删除带头结点的单链表L中所有值为x的结点 */
void Dele_X(LinkList &L, ElemType x){
    LNode *pre = L, *p = L->next, *q;
    while (p != NULL){
        if (p->data == x){
            q = p;
            p = p->next;
            pre->next = p->next;
            free(q); 
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }
}

/* 3. 反向输出单链表L结点的值 */
void R_Print(LinkList L){
    if (L->next != NULL)
        R_Print(L->next);
    if(L != NULL)
        printf("%d", L->data);
}

/* 4. 删除带头结点的单链表中的最小值(唯一) */
void Dele_Min(LinkList &L){
    LNode *pre = L, *p = L->next;       //pre指向前驱结点, p为工作指针
    LNode *minPre = pre, *minP = p;     //最小结点前驱及最小结点
    while (p != NULL){
        if (p->data < minP->next){
            minPre = pre;
            minP = p;
        }
        p = p->next;
        pre = p;
    }
    minPre->next = minP->next;
    free(minP);
}

/* 5. 将带头结点的单链表就地逆置 */
void Reverse(LinkList &L){
    //采用头插法逆置
    LNode *p = L->next, *r;
    L->next = NULL;
    while(p != NULL){
        r = p->next;
        p->next = L->next;
        L->next = p;
        p = r;
    }
}

/* 6. 带头结点的单链表,使其元素递增有序 */
void Sort(LinkList &L){
    LNode *p = L->next, *pre;
    Lnode *r = p->next;
    p->next = NULL;
    p = r;
    while(p != NULL){
        r = p->next;
        pre = L;
        while(pre->next != NULL && pre->next->data < p->data)
            pre = pre->next;
        p->next = pre->next;
        pre->next = p;
        p = r;
    }
}

/* 7. 删除无序带头结点的单链表中在给定区间的元素 */
void RangeDelete(LinkList &L, int min, int max){
    LNode *pre = L, *p = L->next;
    while(p != NULL){
        if (p->data >= min && p->data <= max){
            pre->next = p->next;
            p = p->next;
            free(p);
        }
        else{
            pre = p;
            p = p->next;
        }
    }
}

/* 8. 给定两个单链表,找出这两个结点的公共结点 */
LinkList Search_1st_Common(LinkList L1, LinkList L2){
    int len1 = Length(L1), len2 = Length(L2), dis;
    LinkList longList, shortList;
    if (len1 < len2){
        longList = L2;
        shortList = L1;
        dis = len2 - len1;
    }
    else{
        longList = L1;
        shortList = L2;
        dis = len1 - len2;
    }
    while(dis--)
        longList = longList->next;
    while(longList != NULL){
        if(shortList == longList)
            return longList;
        else{
            longList = longList->next;
            shortList = shortList->next;
        }
    }
    return NULL;
}

/* 9. 按递增次数输出带头结点单链表,并释放结点所占空间(不允许用数组做辅助空间) */
void Min_Dele(LinkList &head){
    LNode *pre, *p, *u;
    while(head->next != NULL){
        pre = head;
        p = pre->next;
        while(p != NULL){
            if (p->next->data < pre->next->data)
                pre = p;
            p = p->next;
        }
        printf(pre->next->data);
        u = pre->next;
        pre->next = u->next;
        free(u);
    }
    free(head);
}

/* 10. 将带头结点的单链表A分解为两个带头结点的单链表A和B,使得A中为奇数序号的元素,B为偶数序号元素 */
LinkList DisCreat(LinkList A){
    int i = 0;
    LNode *B, *ra = A, *rb = B, *p;     //ra和rb指向A,B的尾节点
    B->next = NULL;
    p = A->next;
    A->next = NULL;
    while(p != NULL){
        i++;
        if (i % 2 == 0){
            rb->next = p;
            rb = p;
        }
        else{
            ra->next = p;
            ra = p;
        }
        p = p->next;
    }
    ra->next = rb->next = NULL;
    return B;
}

/* 11. 在10题的基础上将B逆置,即B采用头插法 */
LinkList DisCreat(LinkList A){
    LNode *B, *ra = A, *p, *q;     //ra和rb指向A,B的尾节点
    B->next = NULL;
    p = A->next;
    A->next = NULL;
    while(p != NULL){
        ra->next = p;
        ra = p;
        p = p->next;
        if (p != NULL)
            q = p->next;
        p->next = B->next;
        B->next = p;
        p = q;
    }
    ra->next = NULL;
    return B;
}

/* 12. 去掉递增有序单链表中重复值的元素 */
void Dele_Same(LinkList &L){
    LNode *p = L->next, *q;
    if (p == NULL)  return;
    while(p->next != NULL){
        q = p->next;
        if (p->data == q->data){
            p->next = q->next;
            free(q);
        }
        else
            p = p->next;
    }
}

/* 13. 将两个递增单链表合成一个递减单链表 */
void Merge(LinkList &L1, LinkList &L2){
    LNode *r, *p1 = L1->next, *p2 = L2->next;
    L1->next = NULL;
    while (p1 && p2){
        if (p1->data <= p2->data){
            r = p1->next;
            p1->next = L1->next;
            L1->next = p1;
            p1 = r;
        }
        else{
            r = p2->next;
            p2->next = L2->next;
            L2->next = p2;
            p2 = r;
        }
    }
    if (p1) p2 = p1;
    while(p2){
        r = p2->next;
        p2->next = L1->next;
        L1->next = p2;
        p2 = r;
    }
    free(L2);
}

/* 14. 从两个递增单链表中的公共元素产生单链表C */
LinkList Get_Common(LinkList A, LinkList B){
    LNode *p = A->next, *q = B->next, *r, *s;
    LinkList C = (LinkList)malloc(sizeof(LNode));
    r = C;
    while(p && q){
        if (p->data < q->data)
            p = p->next
        else if(p->data > q->data)
            q = q->next;
        else{
            s = (LNode*)malloc(sizeof(LNode));
            s->data = p->data;
            r->next = s;
            r = s;
            p = p->next;
            q = q->next;
        }
    }
    r->next = NULL;
    return C;
}

/* 15. 两个单链表A和B递增,求交集放于A中 */
LinkList Union(LinkList A, LinkList B){
    LNode *pa, *pb, *pc, *u;
    pa = A->next;
    pb = B->next;
    pc = A;
    while(pa && pb){
        if (pa->data == pb->data){
            pc->next = pa;
            pc = pa;
            pa = pa->next;
            pb = pb->next;
            free(u);
        }
        else if(pa->data < pb->data){
            u = pa;
            pa = pa->data;
            free(u);
        }
        else{
            u = pb;
            pb = pb->next;
            free(pb);
        }
        while(pa){
            u = pa;
            pa = pa->next;
            free(u);
        }
        while(pb){
            u = pb;
            pb = pb->next; 
            free(pb);
        }
    }
    pc->next = NULL;
    free(B);
    return A;
}

/* 16. 两个单链表A和B, 判断B是否为A 的连续子序列 */
bool Pattern(LinkList A, LinkList B){
    LNode *p = A, *pre = p, *q = B;
    while(p && q){
        if (p->data == q->data){
            p = p->next;
            q = q->next;
        }
        else{
            pre = pre->next;
            p = pre;
            q = B;
        }
    } 
    if (!q) return true;
    else return false;
}

/* 17. 判断带头结点的循环双链表是否对称 */
bool symmetry(DLinkList L){
    DNode *p = L->next, *q = L->prior;
    while(p != q && q->next !=p){
        if (p->data == q->data){
            p = p->next;
            q = q->prior;
        }
        else return false;
    }
    return true;
}

/* 18. 两个循环单链表A和B,将B连接到A之后,仍保持循环链表形式 */
LinkList Link(LinkList A, LinkList B){
    LNode *p, *q;
    p = A;
    while(p->next != A)
        p = p->next;
    q = B;
    while (q->next !=B)
        q = q->next;
    p->next = B;
    q->next = A;
    return A;
}

/* 19. 重复找到带头结点的循环单链表中的最小值输出并删除,直至单链表为空再删除头结点 */
void Dele_Min(LinkList &L){
    LNode *p, *pre, *minp, *minpre;
    while(L->next != NULL){
        p = L->next;
        pre = L;
        minp = p;
        minpre = pre;
        while (p != L){
            if (p->data < minp->data){
                minp = p;
                minpre = pre;
            }
            pre = p;
            p = p->next;
        }
        printf("%d", *minp);
        minpre->next = minp->next;
        free(minp);
    }
    free(L);
}

/* 20. 对带头指针L带头结点的非循环双向链表,freq访问频度域(初值为0,每调用Locate(L,x)一次就加1),
    同时链表按freq递减排序,最近访问结点排在相同频度之前 */
DLinkList Locate(DLinkList L, int x){
    DNode *p = L->next, *q;   //q为p的前驱
    while(p && p->data != x)
        p = p->next;
    if (!p){
        printf("不存在值为x的结点\n");
        exit(0);
    }
    else{
        p->freq++;
        if (p->next != NULL)
            p->next->prior = p->prior;
        p->prior->next = p->next;
        q = p->prior;
        while(q != L && q->freq <= p->freq)
            q = q->prior;
        p->next = q->next;
        q->next->prior = p;
        p->prior = q;
        q->next = p;
    } 
    return p;
}

/* 21. 带头指针的单链表,查找链表中倒数第k个位置的结点,若查找成功,输出date域的值,并返回1, 否则只返回0*/
/* 设计思想:两个指针片p,q。p先移动k个位置后同步移动p,q */
int Search(LinkList list, int k){
    LNode *p = list->next, *q = list->next;
    int count = 0;
    while(p){
        if (count < k) count++;
        else  q = q->next;
        p = p->next;
    }
    if (count < k) return 0;
    else{
        printf("%d", q->data);
        return 1;
    }
}

/* 22. 带头结点的单链表存储单词,当两个单词有相同后缀是,可共享相同后缀的存储空间。设str1和str2分别指向
    两个单词所在单链表的头结点 ,找出str1 和 str2所指向两个链表公共后缀的起始位置*/
/* 设计思想:1.求出两表长度分别为m,n
             2.让长的那个先走|m-n|+1个节点
             3.之后同时移动两个指针,当两个指针同时指向同一地址时结束               
*/
int Length(LinkList L){
    int count = 0;
    LNode *p = L->next;
    while (p){
        count++;
        p = p->next;
    }
    return count;
}
LNode* Find_Addr(LinkList str1, LinkList str2){
    int m, n, count = 0;;
    LNode *p = str1->next, *q = str2->next;
    m = Length(str1);
    n = Length(str2);
    if (m > n)
        for (int i = 0; i < m-n+1; i++)
            str1 = str1->next;
    else
        for (int i = 0; i < n-m+1; j++)
            str2 = str2->next;
    while (str1 != str2){
        p = p->next;
        q = q->next;
    }
    return p->next;
}   
/* 时间复杂度O(len1 + len2) */

/* 23. 带头结点的单链表保存m个整数,删除绝对值相同的结点,仅保留第一次出现的结点 */
/* 设计思想:空间换时间,设置辅助数组来保存第一次出现的数 */
void Delete(LinkList &L, int n){
    LNode *p = L, *r;
    int *a;
    a = (int *)malloc(sizeof(int)*(n+1));
    memset(a, 0, sizeof(a));
    while(p->next != NULL){
        int m = abs(p->next->data);
        if (*(a+m) == 0){
            *(a+m) = 1;
            p = p->next;
        }
        else{
            r = p->next;
            p->next = r->next;
            free(r);
        }
    }
    free(a);
}
/* 时间复杂度O(m), 空间复杂度O(n) */

/* 24. 判断一个链表是否有环,如果有,找出环的入口并返回,否则返回NULL */
LNode* FindLoopStart(LNode *head){
    LNode *fast = head, *slow = head;
    while (slow != NULL && fast->next !=NULL){
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) break;
    }
    if (slow == NULL || fast->next == NULL)
        return NULL;
    LNode *p1 = head, *p2 = slow;
    while(p1 != p2){
        p1 = p1->next;
        p2 = p2->next;
    }
    return p1;
}

/* 25. 带头结点的单链表中{A1, A2, ..., An},重新排列结点使其成为{A1, An, A2, An-1,...},
    要求空间复杂度为O(1) */
/* 设计思想:1. 后半段原地逆置,设指针p和q,p每次走一步,q每次走两步,当q到结尾时,p指向中间节点
            2.依次从前后两段各取一个结点按要求重排
 */
void Change_List(LNode *h){
    LNode *p, *q, *r, *s;
    p = q = h;
    while(q->next != NULL){
        p = p->next;
        q = q->next;
        if (q->next != NULL) q = q->next;
    }
    q = p->next;    // p指向中间节点,q指向后半段首节点
    p->next = NULL;
    while(q != NULL){
        r = q->next;
        q->next = p->next;
        p->next = q;
        q = r;
    }
    s = h->next;    //s 指向前半段第一个数据结点
    q = p->next;    //q 指向后半段第一个数据结点
    p->next = NULL;
    while(q != NULL){
        r = q->next;
        q->next = s->next;
        s->next = q;
        s = q->next;
        q = r;
    }
}

 

上一篇:将两个循环链表连接并使得连接后依然是循环链表


下一篇:C语言 链表基础--整表创建