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

选项删除第一个元素的时间复杂度插入新元素
AO(1)O(n)
BO(1)O(1)
CO(1)O(n)
DO(1)O(1)
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theskylife

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值