数据结构学习笔记之线性表

顺序表

1 两个有序表合并

//将两个有序的顺序表 A 和 B 合并为一个有序表 C
void MergeList(int *A, int *B, int *C, int ALen, int BLen, int CLen){
    int i = 0, j = 0, k = 0;
    // 依次比较 A 和 B 中元素的大小,依次放入 C 表,若有一表结束,另一表直接连接在 C 表末尾
    while (i < ALen && j < BLen){
        if (A[i] <= B[j])
            C[k++] = A[i++];
        else
            C[k++] = B[j++];
    }
    //拼接剩余段
    while (i < ALen) C[k++] = A[i++];
    while (j < BLen) C[k++] = B[j++];
}

2 就地逆转

//将有序顺序表进行逆转
void Reverse(int *A, int Alen){
    int mid = Alen/2;
    for (int i = 0; i < mid; ++i) {
        int temp = A[i];
        A[i] = A[Alen - i - 1];
        A[Alen - i - 1] = temp;
    }
}

3 根据枢纽调整

//调整表 L,使左边元素均小于 temp,右边均大于等于 temp
void Adjust(int *L, int Llen, int temp){
    int help[Llen];
    int low = 0;
    int high = Llen - 1;
    for (int i = 0; i < Llen; ++i) //将 L 中的元素依次和 temp 比较
        if (L[i] < temp)
            help[low++] = L[i]; // L[i] < temp,从左向右存放
        else
            help[high--] = L[i];    // 反之从右至左存放
    for (int j = 0; j < Llen; ++j) // 临时顺序表 help 中的元素依次复制到原表 L
        L[j] = help[j];
}

4 顺序表主元素

//在一个顺序表中,元素值满足 0 <= ai < n(其中 n 为元素个数),规定若一个数出现的次数大于 n/2 ,则成为该顺序表的主元素
//[例] {5,1,2,5,5,1,6,2,5,5,5}   主元素为 5
int MainElement(int *A, int Len){
    //因为元素值大小不会超过总元素个数 n,因此可以使用一个长度为 n 的数组来记录对应的数值均出现了几次
    int *count = (int *)malloc(sizeof(int) * Len);
    memset(count, 0, sizeof(int) * Len);
    for (int i = 0; i < Len; ++i)
        count[A[i]]++;

    for (int j = 0; j < Len; ++j)
        if (count[j] > (int)(Len/2))
            return j;
        else return -1;
}

5 循环左移元素

//将一个储存有 n (>1) 个元素的顺序表中的元素,循环左移 p 个单位
//[例] {1,2,3,4,5,6}     左移 3 位      {4,5,6,1,2,3}
void RotateLeft(int *A, int p, int Len){
    int remove = p % Len;   //remove 值就是新数组首元素在原数组中的下标
    int *copy = (int *)malloc(sizeof(int) * Len);   //将原数组复制一份
    for (int i = 0; i < Len; ++i)
        copy[i] = A[i];
    //填入原数组后段元素
    int count = 0;
    for (int j = remove; j < Len; ++j)
        A[count++] = copy[j];
    //填入原数组前段元素
    for (int k = 0; k < remove; ++k)
        A[count++] = copy[k];
    free(copy);
}

单链表

1 初始化

void InitLinkList(struct ListNode *L){
    struct ListNode *ptr = L;
    ptr->val = INT_MAX;
    ptr->next = NULL;
}

2 头插法

void HeadCreatList(struct ListNode *L, int *elemt, int elemtSize){
    for (int i = 0; i < elemtSize; ++i) {
        struct ListNode *new = (struct ListNode *)malloc(sizeof(struct ListNode));
        new->val = elemt[i];
        new->next = L->next;
        L->next = new;
    }
}

3 尾插法

void TailCreatList(struct ListNode *L, int *elemt, int elemtSize){
    for (int i = 0; i < elemtSize; ++i) {
        struct ListNode *new = (struct ListNode *)malloc(sizeof(struct ListNode));
        new->val = elemt[i];
        new->next = NULL;
        L->next = new;
        L = L->next;    //尾结点 L 后移
    }
}

4 就地逆转

//就地逆转链表
void ReverseLinkList(struct ListNode *L){
    //先将头结点摘下,然后剩余元素使用头插法插入
    struct ListNode *ptr, *r;
    ptr = L->next;
    L->next = NULL; //摘下头结点
    while (ptr){
        r = ptr->next;
        ptr->next = L->next;
        L->next = ptr;
        ptr = r;
    }
}

5 按序输出

//按照递增次序输出带头结点的单链表中的数据元素,并释放节点所占用的空间,要求不使用额外的辅助数组
void PrintIncrease(struct ListNode *L){
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    while (L->next != NULL){
        cur = L->next;
        pre = L->next;
        int temp = INT_MAX;  //temp 初值设置为最大
        //工作指针 cur 依次对单链表元素进行遍历,temp 记录当前的最小值
        //先找到当前链表元素中的最小值
        while (cur->next != NULL){
            cur = cur->next;
            if (cur->val < temp)
                temp = cur->val;
        }
        //如果只剩下最后一个结点,不用定位前驱,直接输出后,释放所有结点空间
        if (L->next->next == NULL){
            printf("%d", L->next->val);
            struct ListNode *re = (struct ListNode *)malloc(sizeof(struct ListNode));
            L->next = NULL;
            free(re);
            free(pre);
            free(cur);
        }
        //找到最小值后,准备删除该结点,使用 pre 定位其前驱结点
        while (pre->next->val != temp)
            pre = pre->next;
        //进行结点的输出与删除
        printf("%d ", temp);
        struct ListNode *s = (struct ListNode *)malloc(sizeof(struct ListNode));
        s = pre->next;
        pre->next = s->next;
        free(s);    //释放结点空间
    }
}

6 删除特定结点

//在带头结点的单链表中,删除所有值为 data 的结点,并释放其空间,假设值为 data 的结点不唯一
void DeleteKeyNode(struct ListNode *L, int data){
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    _Bool isChange = 0; //记录链表中是否还有值为 data 的结点
    while (!isChange) {    //根据 isChange 来决定是否继续删除
        cur = L->next;
        pre = L;
        //查找是否还有值为 data 的结点
        while (cur != NULL)
            if (cur->val == data) {
                isChange = 1;
                break;
            }else{
                cur = cur->next;
            }
        //如果此时 isChange 仍旧为 0,说明已经不存在值为 data 的结点,删除结束
        if (!isChange)
            return;
        //定位到删除结点的前驱
        while (pre->next->val != data)
            pre = pre->next;
        struct ListNode *s = (struct ListNode *) malloc(sizeof(struct ListNode));
        s = pre->next;
        pre->next = s->next;
        free(s);
        //一次操作结束,isChange 归 0
        isChange = 0;
    }
}

7 单链表去重

//在一个递增有序的单链表中,有着相同的重复数值,删除链表中的重复数值
//[例] {7, 10, 10, 15, 18, 18, 19}    {7, 10, 15, 18, 19}
void RemoveDuplicates(struct ListNode *L) {
    struct ListNode *cur = (struct ListNode *) malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *) malloc(sizeof(struct ListNode));
    //两个指针一前一后同时后移,若遇到两指针所指结点数值相同,删除后一个结点
    //cur指向 cur->next,继续重复直至到达链表末尾
    cur = L->next->next;
    pre = L->next;
    //只有一个结点
    if (cur == NULL)
        return;
    while (cur) {
        //两指针所指结点数值相同,需要删除
        if (cur->val == pre->val) {
            struct ListNode *dis = (struct ListNode *) malloc(sizeof(struct ListNode));
            dis = cur;
            cur = cur->next;
            pre->next = cur;
            free(dis);
        } else {
            cur = cur->next;
            pre = pre->next;
        }
    }
}

8 删除介值元素

//在一个带头节点的单链表中所有数值无序存放,删除其中所有介于给定两值之间的元素
void DeleteInterValue(struct ListNode *L, int left, int right){
    //将链表中的元素依次判定是否位于区间中,若符合,删除即可
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    pre = L;
    cur = pre->next;
    while (cur){
        if (cur->val > left && cur->val < right){
            struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
            dis = cur;
            cur = cur->next;
            pre->next = cur;
            free(dis);
        } else{
            cur = cur->next;
            pre = pre->next;
        }
    }
}

9 分解单链表

//将一个带头结点的单链表 A 分解为两个带头结点的单链表 B 和 C
//A中含有原表中序号为奇数的元素,B表中含有原表序号为偶数的元素
void SplitLinkList(struct ListNode *A, struct ListNode *B, struct ListNode *C){
    struct ListNode *cur1 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *cur2 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *cur3 = (struct ListNode *)malloc(sizeof(struct ListNode));
    cur1 = A->next;
    cur2 = B;
    cur3 = C;
    int count = 1;
    while (cur1){
        struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
        node->next = NULL;
        node->val = cur1->val;
        if (count % 2){ //奇数结点
            cur2->next = node;
            cur2 = cur2->next;
        } else{ //偶数结点
            cur3->next = node;
            cur3 = cur3->next;
        }
        cur1 = cur1->next;
        count++;
    }
}

10 递增归并为递减

//将两个元素值递增排列的单链表 A 和 B,归并为一个按元素值递减排列的单链表,要求不使用额外的存储空间
struct ListNode* IncreaseAndDecrease(struct ListNode *A, struct ListNode *B){
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));  //工作指针
    struct ListNode *curA = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *curB = (struct ListNode *)malloc(sizeof(struct ListNode));
    curA = A->next;
    curB = B->next;
    A->next = NULL;     //将 A 的头结点摘下,用来存放最后合并的链表

    while (curA && curB){   //两链表均不为空
        if (curA->val <= curB->val){
            cur = curA->next;   //cur 记录 curA 的后继结点,防止断链
            curA->next = A->next;   //新节点头插法插入 A 链表
            A->next = curA;
            curA = cur;     //工作指针 curA 后移
        } else{
            cur = curB->next;   //记录 curB 的后继,防止断链
            curB->next = A->next;   //新节点头插法插入 A 链表
            A->next = curB;
            curB = cur;     //工作指针 curB 后移
        }
    }
    if (curA)
        curB = curA;
    while (curB){       //剩余结点继续使用头插法插入 A 链表
        cur = curB->next;
        curB->next = A->next;
        A->next = curB;
        curB = cur;
    }
    free(B);
}

11 公共元素建表

//A 和 B 是两个递增有序的带头结点的单链表,利用两者的公共元素产生单链表 C,要求不破坏 A、B 的结点
void PublicBuildList(struct ListNode *A, struct ListNode *B, struct ListNode *C, int ALen, int BLen){
    //先求两表长,以短的为基准,依次将短链表的结点值与长链表比较,出现相等的值,建立新链表结点
    //若短链表遍历结束,表示后续比较不可能出现相同元素,建表结束
    struct ListNode *curTemp = (struct ListNode *)malloc(sizeof(struct ListNode));  //基准表的工作指针
    struct ListNode *curOther = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *curRes = (struct ListNode *)malloc(sizeof(struct ListNode));
    curRes = C;

    if (ALen <= BLen){
        curTemp = A->next;
        curOther = B->next;
    }else{
        curTemp = B->next;
        curOther = A->next;
    }

    while (curTemp && curOther){
        //基准表数值小于比较表,基准表指针后移查看是否有相同元素
        if (curTemp->val < curOther->val)
            curTemp = curTemp->next;
        //基准表数值大于比较表,说明比较表不可目前不可能存在和基准表相同的元素
        //比较表指针后移,将下一个元素与基准表进行比较
        else if(curTemp->val > curOther->val)
            curOther = curOther->next;
        else{  //两指针所指元素相同,建表,然后两指针同时后移
            struct ListNode *res = (struct ListNode *)malloc(sizeof(struct ListNode));
            res->val = curTemp->val;
            res->next = NULL;
            curRes->next = res;
            curRes = curRes->next;
            curTemp = curTemp->next;
            curOther = curOther->next;
        }
    }
}

12 判断是否为子序列

//有两个序列已经存放于单链表 A 和 B 中,判断 B 是否为 A 的连续子序列
int IsSub(struct ListNode *A, struct ListNode *B, int ALen, int BLen){
    struct ListNode *curOri = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *curSub = (struct ListNode *)malloc(sizeof(struct ListNode));
    if (ALen <= BLen){  //取两者中长度较小者作为子序列
        curOri = B->next;
        curSub = A->next;
    } else{
        curOri = A->next;
        curSub = B->next;
    }

    //将短的序列第一个结点值依次与长的结点值比较
    while (curOri){
        if (curSub->val != curOri->val){
            curOri = curOri->next;
            if (curOri == NULL) //较长者遍历结束,仍没有相等的结点,则不可能存在子序列
                return 0;
        } else{ //存在相等结点
            while (curOri && curSub){
                if (curSub->val == curOri->val){    //对应元素相等,两指针同时后移继续比较
                    curSub = curSub->next;
                    curOri = curOri->next;
                    if (curSub == NULL) //较短者遍历结束,是子序列
                        return 1;
                }else   //出现不同,说明非子序列
                    return 0;
            }
        }
    }
}

13 删除绝对值相等结点

//一个链表包含 m 个结点,对于该链表中绝对值相等的结点,仅保留第一次出现的结点
//删除其余绝对值相等的结点,其中结点绝对值不会超过 n
//[例] 21 -> -15 -> -15 -> 8 -> 15           21 -> -15 -> 8
void RemoveAbsEqual(struct ListNode *L, int n){
    struct ListNode *ptr = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
    ptr = L;
    int *help, m;
    help = (int *)malloc(sizeof(int) * (n + 1));
    //初始化辅助数组,标记全部置 0
    for (int i = 0; i < n + 1; ++i)
        *(help + i) = 0;
    while (ptr->next){
        //后方出现的绝对值相等的元素均设置为首先出现元素的相反数
        m = ptr->next->val > 0 ? ptr->next->val : -ptr->next->val;
        if (*(help + m) == 0){  //两数相加为 0,说明绝对值相同
            *(help + m) = 1;    //该数对应的标记置为 1,表示已经有该元素
            ptr = ptr->next;
        } else{
            dis = ptr->next;    //删除链表后方标记为 1 的多余元素
            ptr->next = dis->next;
            free(dis);
        }
    }
    free(help);
}

14 求公共后缀

//求出两个单链表的公共后缀,返回公共后缀的起始地址
struct ListNode* PublicSuffix(struct ListNode *A, struct ListNode *B, int ALen, int BLen){
    //两者中的较长者先走完两者的差值的路程,因为这段路程之内两者不可能指向同一地址
    struct ListNode *curLong = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *curShort = (struct ListNode *)malloc(sizeof(struct ListNode));
    if (ALen <= BLen){
        curShort = A->next;
        curLong = B->next;
    } else{
        curShort = B->next;
        curLong = A->next;
    }
    //较长者先走完差值路程
    int mid = abs(ALen - BLen);
    while (mid){
        curLong = curLong->next;
        mid--;
    }
    //消除差值后,两者一起走,所指地址相同时即为公共后缀起始地址
    while (curLong != curShort){
        curLong = curLong->next;
        curShort = curShort->next;
    }
    return curLong;
}

15 查找倒数第 k 结点

//假设一个链表只给出了头指针,不改变链表的前提下,查找链表中倒数第 K 个位置上的结点,返回该结点的值
int KthNodeFromTheBottom(struct ListNode *L, int k, int Len){
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    cur = L->next;
    int mid = Len - k;  //等价于查找正数第 length - k 个结点
    while (mid){
        cur = cur->next;
        mid--;
    }
    return cur->val;
}

16 链表是否有环

//若一个链表有环,返回环的入口点
struct ListNode* isCircle(struct ListNode *L){
    //快慢指针探测法,快指针步长为 2,慢指针步长为 1,若有环,则两个指针必定在某一时刻指向同一地址
    struct ListNode *slow = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *fast = (struct ListNode *)malloc(sizeof(struct ListNode));
    slow = L;
    fast = L;
    while (fast->next->next){
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
            return slow;
    }
    //否则无环
    return NULL;
}

17 重构单链表

//假设单链表带头结点,存储方式为 {a1, a2, ..., an},将其调整为 {a1, an, a2, an-1, ...}
void RefactorList(struct ListNode *L, int Len){    
    //将原链表中的元素分解,存储为两个顺序表,然后再依次重组成单链表    
    int *A = (int *)malloc(sizeof(int) * Len);    
    int *B = (int *)malloc(sizeof(int) * Len);    
    struct ListNode *res= (struct ListNode *)malloc(sizeof(struct ListNode));    
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));    
    res = L;    
    //将原链表的头结点摘下    
    cur = L->next;    
    //原链表转化为顺序表    
    int cou = 0;    
    while (cur){        
        A[cou] = cur->val;        
        cou++;        
        cur = cur->next;    
    }    
    //顺序表中的元素相向遍历,存储进新的顺序表    
    int count = 0;    
    for (int i = 0, j = Len - 1; i <= j; ++i, --j) {        
        B[count++] = A[i];        
        B[count++] = A[j];    }    //新的顺序表转链表    
        for (int k = 0; k < Len; ++k) {        
            struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));        
            dis->val = B[k];        
            dis->next = NULL;        
            res->next = dis;        
            res = res->next;    
        }
}

18 链表重排列

//已知带头结点的单链表 L,将该链表按结点数据域的值的大小从小到大重新链接
//要求链接过程中不得使用除链表以外的任何链结点空间
void insertsort(struct ListNode *L)
{//插入排序思想
    struct ListNode *p,*q,*pre;
    p = L->next->next;
    L->next->next = NULL;
    while(p)
    {
        q = p->next;
        pre = L;
        while(pre->next != NULL && pre->next->val < p->val)
            pre = pre->next;
        p->next = pre->next;
        pre->next = p;
        p = q;
    }
}

19 删除最小结点

//在带头节点的单链表中删除(仅一个)最小值结点的高效算法
void DeleteMinElement(struct ListNode *L){
    //cur工作指针,pre 始终指向最小值的前驱结点, temp 记录当前最小值
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    cur = L->next;
    pre = L;
    int temp = INT_MAX;
    //找到最小值结点
    while (cur){
        if (cur->val < temp)
            temp = cur->val;
        cur = cur->next;
    }
    //定位到最小值结点的前驱结点
    while (pre->next->val != temp)
        pre = pre->next;
    //删除该结点
    struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
    dis = pre->next;
    pre->next = pre->next->next;
    free(dis);
}

20 是否中心对称

//判断单链表 L 的前 n 个字符是否中心对称(是回文)
_Bool IsItSymmetrical(struct ListNode *L, int n){
    //为便于比较,将链表前 n 个元素转为数组
    int *list = (int *)malloc(sizeof(int) * n);
    memset(list, 0, sizeof(int) * n);
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    cur = L->next;
    int count = n;
    while (count && cur){
        list[n - count] = cur->val;
        cur = cur->next;
        count--;
    }
    //将数组中的元素相向比较,出现不同,则说明不是中心对称
    for (int i = 0, j = n-1; i <= j; ++i, --j)
        if (list[i] != list[j])
            return 0;
    return 1;
}

21 两链表的差集

//已知递增有序的单链表 A,B 分别存储一个集合,设计算法求出两个集合 A 和 B 的差集 A-B(即仅由在 A 中出现
//而不在 B 中出现的元素所构成的集合),并以同样的形式存储,同时返回该集合的元素个数
void SetDifference(struct ListNode *A, struct ListNode *B, struct ListNode *C){
    struct ListNode *curA = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *curB = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *curC = (struct ListNode *)malloc(sizeof(struct ListNode));
    curA = A->next;
    curB = B->next;
    curC = C;
    while (curA && curB){   //A 、B两结点均不为空
        if (curA->val < curB->val){
            //A 小于 B,说明在 B 后序结点中不可能出现与 A 相同的值
            //则该结点符合要求,构造链表,A 指针后移,B 不动
            struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
            dis->val = curA->val;
            dis->next = NULL;
            curC->next = dis;
            curC = curC->next;
            curA = curA->next;
        } else if (curA->val == curB->val){ //A值等于B值,不合要求,排除,两者的工作指针均后移
            curA = curA->next;
            curB = curB->next;
        }else   //A 值大于 B 值, B 的工作指针后移
            curB = curB->next;
    }
    if (!curA)  //模板链表当前元素为空,说明 A 中元素已经比较完,程序结束
        return;
    if (!curB){ //对照链表当前元素为空,说明 A 中剩余元素均符合,构造链表
        while (curA){
            struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
            dis->val = curA->val;
            dis->next = NULL;
            curC->next = dis;
            curC = curC->next;
            curA = curA->next;
        }
    }
}

22 特殊要求链表1

//已知一个单链表中每个结点存放一个整数,并且结点数不少于 2,请设计算法以判断该链表中第二项
//起的每个元素值是否等于其序号的平方减去其前驱的值,若满足则返回 ture,否则返回 false
_Bool SpecialLinkedList_01(struct ListNode *L){
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    pre = L->next;
    cur = L->next->next;
    int index = 2;
    while (cur){
        if (cur->val == (index * index - pre->val)){
            cur = cur->next;
            pre = pre->next;
            index++;
        } else return 0;
    }
    return 1;
}

23 特殊要求链表2

//设有一个由正整数组成的无序单链表,编写完成下列功能的算法:
//(1)找出最小值结点,且打印该数值
//(2)若该数值是奇数,则将其与直接后继结点的数值交换
//(3)若该数值是偶数,则将其直接后继结点删除
void SpecialLinkedList_02(struct ListNode *L){
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    pre = L->next;
    cur = L->next;
    int temp = INT_MAX;
    //找到最小值
    while (cur){
        if (cur->val < temp)
            temp = cur->val;
        cur = cur->next;
    }
    printf("Min Node: %d\n", temp);
    //定位到最小值结点
    while (pre->val != temp)
        pre = pre->next;
    //数值为奇数,交换数值
    if (temp % 2){
        if (pre->next){
            int te = pre->val;
            pre->val = pre->next->val;
            pre->next->val = te;
        }
    } else{ //数值为偶数,删除后继
        struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
        dis = pre->next;
        pre->next = pre->next->next;
        free(dis);
    }
}

24 特殊要求链表3

//设有一个正整数序列组成的有序单链表(按递增次序有序,且允许有相等的整数存在),编写实现下列功能的算法:
//(1)确定在序列中比正整数 x 大的数有几个(相同的数只计算一次,如序列
//   {4,5,7,7,8,10,11,15,15,16,17,20,20}中比 10 大的数有 5 个)
//(2)在单链表中将比正整数 x 小的数按递减次序排列
//(3)将比正整数 x 大的偶数从单链表中删除
void SpecialLinkedList_03(struct ListNode *L, int Len, int x){
    struct ListNode *cur = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *res = (struct ListNode *)malloc(sizeof(struct ListNode));
    res = L;    //摘下头结点
    cur = L->next;  //工作指针

    //链表转顺序表
    int *list = (int *)malloc(sizeof(int ) * Len);
    memset(list, 0, sizeof(int) * Len);
    int count = 0;  //比 x 小的数字个数
    int index = 0;  //数组下标
    int left = -1;  //记录较小数的末尾下标
    int right = Len; //记录较大数的起始下标
    while (cur){
        list[index] = cur->val;
        if (cur->val < x){  //当前元素小于 x
            left++;
            if (cur->val != cur->next->val) //且不重复
                count++;
        }
        if (cur->val > x)
            right--;
        index++;
        cur = cur->next;
    }
    printf("The count of small than x: %d\n", count);

    //将比 x 小的数按照递减顺序排列,即反转当前的小元素表
    for(int i = 0, j = left; i <= j; ++i, --j){
        int temp = list[i];
        list[i] = list[j];
        list[j] = temp;
    }

    //将比 x 大的数中偶数删除
    int oddInd = 0; //奇数数组的索引
    int *odd = (int *)malloc(sizeof(int ) * (Len - right));
    memset(odd, 0, sizeof(int) * (Len - right));
    //大于 x 的奇数存进新的表中,等于删除了偶数
    for (int k = right; k < Len; ++k)
        if (list[k] % 2)
            odd[oddInd++] = list[k];

    //最终结果生成链表
    for (int l = 0; l < right; ++l) {//链接原链表中小于等于 x 的元素
        struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
        dis->val = list[l];
        dis->next = NULL;
        res->next = dis;
        res = res->next;
    }
    for (int m = right, n = 0; m < Len, n < oddInd; ++m, ++n) { //链接原链表中大于 x 的元素
        struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
        dis->val = odd[n];
        dis->next = NULL;
        res->next = dis;
        res = res->next;
    }
}

25 特殊要求链表4

//已知三个带头结点的线性链表 A、B 和 C 中的结点均依元素值自小至大排列,编写算法对 A 表进行如下操作:
//使操作后的链表 A 中仅留下三个表中均包含的数据元素的结点,且没有值相同的结点,并释放所有无用结点
// 限定算法的时间复杂度为 O(m+n+p),其中 m、n和 p 分别为三个表的长度

//公共元素建表,结果保留在第一个链表中
void BulidListPublic(struct ListNode *L1, struct ListNode *L2){
    struct ListNode *cur1 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *cur2 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    pre = L1;   //始终指向 cur1 的前驱结点
    cur1 = L1->next;
    cur2 = L2->next;

    while (cur1 && cur2){
        //当前 L1 元素小于 L2,则说明当前元素不可能是两者公共元素,cur1 后移,cur2 不动,删除该结点
        if (cur1->val < cur2->val){
            struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
            dis = cur1;
            pre->next = cur1->next;
            cur1 = cur1->next;
            pre = pre->next;
            free(dis);
        }else if (cur1->val > cur2->val){ //当前 L1 元素大于 L2,cur1 不动,cur2 后移
            cur2 = cur2->next;
        } else{ //两者当前元素相同,保留该结点,工作指针同时后移
            cur1 = cur1->next;
            cur2 = cur2->next;
        }
    }
    //L1 和 L2 只要有一个遍历结束,则没有结束的链表中所有元素均不可能成为公共元素,全部删除
    if (cur1){
        struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
        dis = cur1;
        pre->next = cur1->next;
        cur1 = cur1->next;
        free(dis);
    }
}

void SpecialLinkedList_04(struct ListNode *A, struct ListNode *B, struct ListNode *C){
        //先求 A 和 B 的交集,改变 A 链表
        BulidListPublic(A, B);
        //再求 A 和 C 的交集,改变 A 链表
        BulidListPublic(A, C);
}

26 特殊要求链表5

//已知两个单链表 A 和 B,其头指针分别为 heada 和 headb,编写一个过程从单链表 A 中删除自第 i 个元
//素起的共 len 个元素,然后将单链表 A 插入到单链表 B 的第 j 个元素之前
void SpecialLinkedList_05(struct ListNode *A, struct ListNode *B, int start, int len, int insert){
    struct ListNode *cur1 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *cur2 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    int startIndex = 1;     //指示 A 链表中删除的起始位置
    int removeCount = 0;    //指示 A 中删除元素的长度
    int insertIndex = 1;    //指示 B 中插入元素的位置
    cur1 = A->next;
    cur2 = B->next;
    pre = A;

    while (cur1){
        while (startIndex != start){//定位到删除位置
            cur1 = cur1->next;
            pre = pre->next;
            startIndex++;
        }
        while (removeCount != len){//开始删除
            struct ListNode *dis = (struct ListNode *)malloc(sizeof(struct ListNode));
            dis = cur1;
            pre->next = cur1->next;
            cur1 = cur1->next;
            free(dis);
            removeCount++;
        }
        break;
    }
    while (cur1->next) //删除后为方便接下来的插入连接操作,将 cur1 移动到 A 的末尾元素
        cur1 = cur1->next;
    while (cur2){
        while (insertIndex != (insert - 1)){ //定位到插入位置的前驱
            cur2 = cur2->next;
            insertIndex++;
        }
        break;
    }
    //插入 A 链表
    struct ListNode *r = (struct ListNode *)malloc(sizeof(struct ListNode));
    r = cur2->next;
    cur2->next = A->next;
    cur1->next = r;
}

27 交换结点

//编写一个算法来交换单链表中指针 P 所指结点与其后继结点,HEAD 是该链表的头指针,P 指向该链表中某一结点
void ExchangeNode(struct ListNode *L, struct ListNode *p){
    struct ListNode *pre = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *post = (struct ListNode *)malloc(sizeof(struct ListNode));
    pre = L;
    post = p->next->next;   //记录 p 的后继的后继,防止断链
    while (pre->next != p)  //定位到 p 的前驱
        pre = pre->next;
    pre->next = p->next;
    p->next->next = p;
    p->next = post;
}

单循环链表

1 尾插法建立

//尾插法建立循环链表
void CreatCirList(struct ListNode *L, int *list, int len){
    struct ListNode *tail, *head;
    L->next = NULL;
    head = L;

    for (int i = 0; i < len; ++i) {
        struct ListNode *new = (struct ListNode *)malloc(sizeof(struct ListNode));
        new->val = list[i];
        L->next = new;
        L = new;
    }
    tail = L;   //将尾结点赋值给尾指针
    tail->next = head->next;     //将尾指针的指针域指向第一个元素
}

2 删除指定结点前驱

//在一循环单链表中,既无头结点也无头指针.s 为指向某个结点的指针,删除 s 所指结点的前驱结点
void RemoveNodePre(struct ListNode *L, struct ListNode *s){
    struct ListNode *pre;
    pre = s;
    //找到待删除结点的前驱
    while (pre->next->next != s)
        pre = pre->next;
    //删除结点
    struct ListNode *dis = pre->next;
    pre->next = s;
    free(dis);
}

3 连接两循环单链表

//假设有两个循环单链表 A 和 B,头指针分别为 hradA 和 headB,将 B 连接到 A 之后,要求连接后仍是循环链表
void MergeCirList(struct ListNode *A, struct ListNode *B, int ALen, int BLen){
    struct ListNode *tailA = A;
    struct ListNode *tailB = B;
    //分别定位到 A 和 B 的尾结点
    while (ALen){
        tailA = tailA->next;
        ALen--;
    }
    while (BLen){
        tailB = tailB->next;
        BLen--;
    }
    //连接两链表
    tailA->next = B->next;  //A 的尾结点下一节点为 B 的第一个结点
    tailB->next = A->next;  //B 的尾结点下一节点为 A 的第一个结点
}

4 循环删除最小值

//一个带头结点的循环链表中的值均为正,反复找出链表中的最小值结点并删除,直至链表为空,最后删除头结点
void RemoveMinCirList(struct ListNode *L, int Len){
    struct ListNode *cur, *curPre, *min, *minPre;
    min = L->next;
    minPre = L;
    int temp = Len;
    while (Len){   //表不为空时进行循环
        cur = L->next;  //工作指针
        curPre = L;     //始终指向工作指针前驱
        while (temp){   //单次循环找最小值
            if (cur->val < min->val){
                min = cur;
                minPre = curPre;
            }
            curPre = cur;       //两个工作指针同时后移
            cur = cur->next;
            temp--;
        }

        struct ListNode *dis = min;
        min = min->next;
        minPre = minPre->next;
        printf("Remove Node %d\n", dis->val);
        free(dis);  //删除结点空间
        Len--;
    }
    //释放头结点
    free(L);
    printf("Remove Head Node");
}

双向链表

1 尾插法建立

void CreatDulList(struct DulListNode *L, int *list, int Len){
    struct DulListNode *tail;
    L->prior = NULL;
    L->next = NULL;
    tail = L;       //tail 始终指向尾结点
    for (int i = 0; i < Len; ++i) {
        struct DulListNode *new = (struct DulListNode *)malloc(sizeof(struct DulListNode));
        new->val = list[i];
        tail->next = new;
        new->prior = tail;
        tail = new;
    }
    tail->next = NULL;
}

2 插入结点

//在双链表的位置 index 之后插入一个新节点 element
void InsertDulList(struct DulListNode *L, int index, int element){
    struct DulListNode *cur = L->next;
    //定位到插入位置之前的结点
    while (index - 1){
        cur = cur->next;
        index--;
    }
    //插入新结点
    struct DulListNode *new = (struct DulListNode *)malloc(sizeof(struct DulListNode));
    new->val = element;
    new->next = cur->next;
    cur->next->prior = new;
    new->prior = cur;
    cur->next = new;
}

3 删除结点

//删除双链表第 index 位置的元素
void RemoveDulNode(struct DulListNode *L, int index){
    struct DulListNode *cur = L->next;
    struct DulListNode *pre = L;    //始终指向被删除结点的前驱
    //定位到被删节点
    while (index - 1){
        cur = cur->next;
        pre = pre->next;
        index--;
    }
    struct DulListNode *dis = cur;
    pre->next = cur->next;
    cur->next->prior = pre;
    free(dis);
}
posted @ 2021-11-01 16:18  悟道九霄  阅读(163)  评论(0编辑  收藏  举报