C语言中链表的经典习题及相关代码实现

本文使用的链表创建代码如下:

#include<stdio.h>
#include<stdlib.h>
#define N 100
typedef int ElementType;
typedef struct Lnode{
    
    
    ElementType data;
    struct Lnode *next;
}LNode, *Linklist;

Linklist Create_list_tail(Linklist head){
    
    
	//尾插法(含头结点)
    head = (Linklist)malloc(sizeof(LNode));
    Linklist node = NULL;
    Linklist end = NULL;
    head->next = NULL;  //若没有后续结点,则将next值为null
    end = head; //若没有后续结点,则记录尾结点为头结点。
    int count = 0;  //结点个数
    scanf("%d", &count);
    for (int i = 0; i < count; i++){
    
    
        node = (LNode *)malloc(sizeof(LNode));  //为新结点开辟空间
        node->data = i * 0;      //为新的结点数据域赋值
        end->next = node;   // 将新结点的值给上一个结点的next
        end = node;         //将新结点标记为尾结点
    }
    end->next = NULL;      //将尾结点的next值赋为NULL
    return head;
}


1 两个链表的合并

1.1 升序排序

题目描述:
给定键值按升序排列的带头结点的单链表La和Lb,将其合并成升序排列的单链表,并返回新链表的表头指针。要求利用原表的结点数据空间。
实现代码:

LNode * Merge_linklist(Linklist La, Linklist Lb){
    
    
    //对两个含有头结点的升序链表进行合并,合并后仍然为升序。
    LNode *Lc, *pa, *pb, *pc, *ptemp;
    //选择其中一个链表作为首节点,进行初始化,如选择La
    Lc = La;
    pc = La;
    pa = La->next;
    pb = Lb->next;
    while(pa!= NULL && pb!=NULL){
    
    
        if (pa->data < pb->data){
    
    
            pc->next = pa;
            pc = pa;
            pa = pa->next;
        }else if (pa->data > pb->data){
    
    
            pc->next = pb;
            pc = pb;
            pb = pb->next;
        }else{
    
    
            //若两值相等,则取其中一个,并释放另外一个空间
            pc->next = pa;
            pc = pa;
            ptemp = pb;
            pa = pa->next;
            pb = pb->next;
            free(ptemp);
        }
    }
     // 将未到末尾的链表拼接到新链表后面。
    if(pa != NULL) pc->next = pa;
    else pc->next = pb;
     //释放Lb的头结点
    free(Lb);
    return Lc;
}

1.2 降序排序

题目描述:
对两个带头结点的升序链表la和lb进行合并,合并后的链表依旧为升序,要求利用原表的数据空间

LNode * Merge_linklist_reverse(Linklist La, Linklist Lb){
    
    
    //对两个带头结点的升序链表la和lb进行合并,合并后的链表依旧为升序,要求利用原表的数据空间
    LNode *Lc,*pa,*pb,*pc,*ptemp;
    pa = La->next;
    pb = Lb->next;
    Lc = La;
    Lc->next = NULL;
    while(pa != NULL && pb != NULL){
    
    
        if(pa->data < pb->data){
    
    
            pc = pa;
            //先将pa下一个结点找出来,再修改pc,也就是pa
            pa = pa->next;
            pc->next = Lc->next;
            Lc->next = pc;
        }else if (pa->data > pb->data){
    
    
            pc = pb;
            pb = pb->next;
            pc->next = Lc->next;
            Lc->next = pc;
        }else{
    
    
            pc = pa;
            ptemp = pb;
            pa = pa->next;
            pb = pb->next;
            free(ptemp);
            pc->next = Lc->next;
            Lc->next = pc;
        }
    }
    while(pa != NULL){
    
    
        pc = pa;
        pa = pa->next;
        pc->next = Lc->next;
        Lc->next = pc;
    }
    while(pb != NULL){
    
    
        pc = pb;
        pb = pb->next;
        pc->next = Lc->next;
        Lc->next = pc;
    }
    free(Lb);
    return Lc;
}

2.对链表进行去重

对链表中的元素进行去重,要求在原数据空间中操作。
链表中元素打印代码:

void print_list(Linklist head){
    
    
	//打印带头结点链表的所有元素
    LNode *p;
    p = head->next;
    while(p != NULL){
    
    
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
}

2.1 方法1—双循环法

算法实现思路:
利用双循环法进行实现。若链表中首个元素为1,则将后续元素一次与1进行相比,若相同则进行删除,否则进行下一个进行对比;获取链表中第二个元素,若为2,则将后续元素依次与2对比,相同则删除,否则继续。
算法的时间复杂度O(n²),空间复杂度为O(1)

Linklist ElementType_unique(Linklist L){
    
    
    //对带头结点的链表L进行去重,对原表去重后,并返回该链表;
    //算法时间复杂度为O(N**2),空间复杂度为O(n);
    LNode *p, *compr_pre, *compr, *temp;
    p = L->next;
    while (p != NULL){
    
    
        compr = p->next;
        compr_pre = p;
        while(compr != NULL){
    
    
            if(p->data == compr->data){
    
    
                compr_pre -> next = compr->next;
                //compr_pre = compr;
                temp = compr;
                compr = compr->next;
                free(temp);
            }else{
    
    
                compr_pre = compr;
                compr = compr->next;
            }
        }
        p = p->next;
    }
}

2.1 方法2—哈希法

算法实现思路:
该方法仅仅针对数组中元素为整数的情况,利用哈希思想进行实现。假设链表中的最大值为N-1,创建一个长度为N的数组,并将所有元素初始化为0;循环获取链表的值,并写入数组对应的值中,若值大于1,则说明该元素重复,需要进行删除,等于1说明该元素有出现,等于0说明该元素未曾出现。
算法的时间复杂度O(n),空间复杂度为O(N)
实现代码如下:

Linklist Elem_unique_hash(Linklist L){
    
    
    //对带头结点的链表L进行去重(链表元素需要均为整数,且不大于N)
    int arr[N]={
    
    0};
    LNode *p, *m, *temp;
    p = L->next;
    m = L;
    while(p != NULL){
    
    
        //对数字的值进行计数
        arr[p->data]++;
        if (arr[p->data] > 1){
    
    
            //对计数大于1的元素进行删除
            m->next = p->next;
            temp = p;
            p = p->next;
            free(temp);
        }else{
    
    
            m = m->next;
            p = p->next;
        }
    }
    return L;
}

3.顺序表变逆序

将顺序排列带头结点链表L进行逆序排列,要求使用原表空间

LNode * reverse_list(Linklist L){
    
    
    //将顺序排列带头结点链表L进行逆序排列,要求使用原表空间
    LNode * p, * temp;
    p = L->next;
    L->next = NULL;
    temp = NULL;
    while(p!=NULL){
    
    
        /*写法1:注意先后顺序,否则容易造成链表断裂
        temp = p;
        p = p->next;
        temp->next = L->next;
        L->next = temp;*/
        //写法2
        temp = p->next;
        p->next = L->next;
        L->next = p;
        p = temp;
    }
    return L;
}

4.其他

如果对线性表的操作有两种,即删除第一个元素,在最后一个元素的后面插入新元素,那么最好使用:
A. 只有表头指针没有表尾指针的循环单链表;
B.只有表头指针没有表尾指针的循环单链表;
C.非循环单链表;
D.循环单链表;
答案:B

选项 删除第一个元素的时间复杂度 插入新元素
A O(1) O(n)
B O(1) O(1)
C O(1) O(n)
D O(1) O(1)

猜你喜欢

转载自blog.csdn.net/qq_41780234/article/details/127261169
今日推荐