C语言 链表-链表全家桶(附完整代码)香饽饽!!嘎嘎香!!!

单链表由各个内存结构通过一个指针链接在一起组成,每一个内存结构都存在后继内存结构(链尾除外),内存结构由数据域和指针域组成。

下文介绍了链表的一些基本操作,所有代码均在子函数中编写,文章最下方附带完整代码,Copy即可食用。

一、链表的创建

说到链表,肯定少不了结构体,首先第一步我们要创建结构体(头文件不算数嗷)。接下来就要创建头结点,所谓头结点,其实就是创建一个指针来指向一块内存空间 如下文所示:

typedef struct Node{
    int data;                 //数据域(你可以理解为存放数据的位置)
    struct Node *next;        //指针域(存放下一个结点的地址)
}Node;

LinkList create(){
    
    //定义一个指针,为指针 申请一块空间
    Node *head = (Node *)malloc(sizeof(Node));
    
    //判断空间是否开辟成功
    if(!head){
        printf("空间申请失败!\n");
        return NULL;
    }
    
    //头结点,指针域值为 NULL
    head->next = NULL;
    
    //返回 头结点 指针
    return head;
    
}

二、链表的输入

链表的数据(输入/插入)与数组思想一样,利用scanf录入数据值,不同的是数组是直接放入下标位空间,而链表是将所录入的数据放入到了结构体中的数据域中,然后令移动至指针域所指向的下一个结点空间,继续输入,注意 最后一位结点记得将指针域赋空值 即NULL,如下文所示:

void input(Node *head){
    
    //用来判断头结点是否开辟成功 若不成功则结束程序
    if(!head){
        return;
    }
    
    //这里需要注意 链表是通过头结点一步一步移动寻找数据的,所以需要再创建一个指针指向头结点,利用这个指针进行操作,否则会造成数据丢失的情况。
    //创建指针 指向头结点
    Node *q = head;
    
    //这里就以录入5个数据为例
    for(int i=0;i<5;i++){
        Node *p = (Node *)malloc(sizeof(Node));    //开辟新的结点

        printf("请输入数据:");
        scanf("%d",&p->data);        //这里就是输入数据的位置,注意需要放地址
        
        //这里利用的是 尾插法 连接的链表,(尾插法即:将结点连接至链表尾部)
        q->next = p;    //q指向的是头结点,头结点的指针域存放p的地址
        
        //令q结点移动至它本身指针域存放地址的位置 即p的位置,循环结束下一次循环再次创建新的p结点
        q = q->next;    
    }
    
    //这里因为循环录入已经结束,所以将最后一个结点的指针域赋空值
    q->next = NULL;
    
}

三、链表的查找

查找数据即遍历链表并一步步的进行比较,这段比较简答,上代码:

void find(Node *head){
    
    if(!head){
        return;
    }
    
    //头结点所指向的 next 是 "首元结点",即:链表中存储第一个数据元素的节点。
    Node *q = head->next;
    
    int value;
    printf("请输入待查找值:");
    scanf("%d",&value);
    
    //循环条件 q 等价于 q != NULL ,即:q存在则为真 返回1;不存在则 为假 返回0。
    while(q) {
        if(q->data == value){        //若遍历至 数据域数据与待查找值 相同则输出
            printf("%d存在数组中!",value);
            return;
        }
        q = q->next;    //若没有找到 结点移动至下一位 继续循环查找
    }
    
    printf("%d不存在数组中!",value);        //循环结束完毕若没有执行 return 结束程序,则输出未找到。
    
}

四、链表的删除

链表中删除数据 即待删除数据的直接前驱结点指向待删除数据的后一位,即直接前驱结点的指针域存放待删除数据后一位结点的地址。

例:

删除前:A->B->C->D

删除结点B 后:A->C->D

需要注意的是:若要删除结点,必须要知道这个结点的直接前驱结点

直接前驱结点:即 结点 的左边第一个结点,如上例,结点C 就是 结点D 的直接前驱结点。

// 根据 元素值 去删除 这个结点
void delete(LinkList head, int value){
    
    if(!head){
        return;
    }
    
    Node *p = head;            // p 指针 指向 头结点的下一个
    
    Node *q;
    
    while( p->next ){
        
        // 若满足条件,则 p 就是待删除元素结点的直接前驱
        if( p->next->data == value ){
            
            q = p->next;        // 令 q 指向 待删除结点
            
            p->next = q->next;        // 直接前驱结点 指向 待删除结点的 后一位结点
            
            free(q);        // 此时 释放结点
            
        } else{
            //这里写 else 是为了防止两个相邻的元素 都与 待删除元素的值 相同,若只想删除一个则不需要写 else 直接在 if外、while内 写即可
            p = p->next;
            
        }
        
    }
    
}

五、链表的销毁

销毁链表,无非就是 删除结点并将其释放掉 最后再将头结点赋空值,在C语言中将头结点赋空值即会回收空间,但是 严格的来讲 C语言并没有空值这个概念,一般习惯上,指针若是指向地址0便是空值,其他数据若内容是0便是空值。

void destory(Node *head){
    
    if(!head){
        return;
    }
    
    Node *p = head->next;    //创建首元结点指针
    
    //这里与上面同理,判断结点存在则执行
    while(p){
        
        // 创建一个新指针 指向 该结点,用来记录结点
        Node *q = p->next;
        
        // 将 p 释放掉即可
        free(p);
        
        p = q;
        
    }
    
    head->next = NULL;        //使头结点的指针域不在存储地址
    
    free(head);        //然后将其释放掉
    
    head = NULL;        //最后将 head 指针赋空值,系统会回收空间
    
}

六、链表的排序

链表排序思想和数组排序类似,区别就是数组遍历容易,数据交换也容易,但链表不同链表 (单项链表)只能一个方向遍历,不可以逆序遍历,且不能随机访问,所以排序相对而言比较麻烦,同时链表的数据交换也很麻烦,如果交换两个节点,需要共涉及3个节点,无形中增加了时间复杂度,也可以直接交换节点中的数据,这种方式相对与交换结点而已比较简单,如下文所示:

void bubbleSort(Node *head){
    
    //这里判断头结点是否存在,若不存在则结程序
    if(!head){
        return;
    }
    
    Node *q = head;        //创建指针 指向头结点
    
    //这里创建两个同类型指针,用来接收结点的指针域。因为链表中没有下标这一说,所以我们需要替代品进行辅助排序,你们明白就好。
    Node *p;
    Node *p1;
    
    //这里就以最简单的冒泡排序来讲,利用外层循环来控制比较次数。
    for(int i=0; i<5-1 ;i++){
        
        q = head->next;
        
        //内层循环来控制需要比较的数据
        for(int j=0; j< 5-i-1 ; j++){
            
            //这里就用到了上面所创建的两个指针,分别指向q 与 q的后一位。
            p = q;
            p1 = q->next;
            
            //利用所接收到的指针来比较
            if(p->data < p1->data){
                
                //这里利用空杯交换来交换数据域的值,简单来说,就是创建一个变量来接收A的值,然后再将B的值赋于A,最后将最开始创建的变量中的值重新赋给B。
                int temp = p->data;
                p->data = p1->data;
                p1->data = temp;
                
            }
            
            //指针后移。令其指向下一个结点的位置
            q = q->next;
            
        }
        
    }
    
}

七、完整代码块

这里为了检测大家对代码的理解,刻意将所有注释删除掉了,如果需要注释的话看上面的代码块即可,话不多说 上代码:

#include<stdio.h>
#include<stdlib.h>

typedef struct Node{
    int data;
    struct Node *next;
}Node, *LinkeList;

//这里是函数的声明区
LinkList create(Node *head);
void foreach(Node *head);
void insert(Node *head);
void delete_(Node *head);
void find(Node *head);
void bubbleSort(Node *head);
void foreach(Node *head);
void destory(Node *head);

//主函数
int main(){

    Node *head = (Node *)malloc(sizeof(Node));
    head->next = NULL;

    create(head);
    
    foreach(head);

    insert(head);

    delete_(head);

    find(head);

    destory(head);

}

//**************************************

LinkList create(Node *head){
    
    LinkList head = (Node *)malloc(sizeof(Node));
    
    if(!head){
        printf("空间申请失败!\n");
        return NULL;
    }
    
    head->next = NULL;
    
    return head;
    
}

//**************************************

void foreach(Node *head){
    
    if(!head){
        return;
    }
    
    LinkList p = head->next;
    
    while(p){
        printf("%d ",p->data);
        p = p->next;
    }
    
}

//**************************************

void insert(Node *head){
    
    if(!head){
        return;
    }
    
    int num;
    
    printf("请输入要插入的结点数量:");
    scanf("%d",&num);
    
    Node *q = head;
    
    for(int i=0; i<num; i++){
        LinkList p = (Node *)malloc(sizeof(Node));
        
        printf("请输入数据域的值:");
        scanf("%d",&p->data);
        
        q->next = p;
        q = q->next;
        
    }
    
    q->next = NULL;
    
}

//**************************************

void delete_(Node *head){
    
    if(!head){
        return;
    }

    int value;
    printf("请输入带删除元素值:");
    scanf("%d",value);
    
    Node *p = head;
    
    Node *q;
    
    while( p->next ){
        
        if( p->next->data == value ){
            
            q = p->next;
            
            p->next = q->next;
            
            free(q);
            
        } else{
            
            p = p->next;
            
        }
        
    }
    
}

//**************************************

void find(Node *head){
    
    if(!head){
        return;
    }
    
    Node *q = head->next;
    
    int value;
    printf("请输入待查找值:");
    scanf("%d",&value);
    
    while(q) {
        if(q->data == value){
            printf("%d存在数组中!",value);
            return;
        }
        q = q->next;
    }
    
    printf("%d不存在数组中!",value);
    
}

//**************************************

void bubbleSort(Node *head){
    
    if(!head){
        return;
    }
    
    Node *q = head;
    
    Node *p;
    Node *p1;
    
    for(int i=0; i<5-1 ;i++){
        
        q = head->next;
        
        for(int j=0; j< 5-i-1 ; j++){
            
            p = q;
            p1 = q->next;
            
            if(p->data < p1->data){
                
                int temp = p->data;
                p->data = p1->data;
                p1->data = temp;
                
            }
            
            q = q->next;
            
        }
        
    }
    
}

//**************************************

void destory(Node *head){
    
    if(!head){
        return;
    }
    
    Node *p = head->next;
    
    while(p){
        
        Node *q = p->next;
        
        free(p);
        
        p = q;
        
    }
    
    head->next = NULL;
    
    free(head);
    
    head = NULL;
    
}

最后,在这里祝大家的代码永远不报错 ^v^ (其实是在对我说……)

  • 18
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Discord_lim

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

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

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

打赏作者

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

抵扣说明:

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

余额充值