服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言实现线性动态(单向)链表的示例代码

C语言实现线性动态(单向)链表的示例代码

2022-12-02 15:33非线性光学元件 C/C++

本文主要介绍了C语言实现线性动态(单向)链表的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

什么是链表

链表是数据结构里面的一种,线性链表是链表的一种,线性链表的延伸有双向链表和环形链表。在编程语言中优化数据结构可以在处理大数据时大大降低程序的空间复杂性和时间复杂性。这里我只用一个简单的例子——线性单向链表为例,说明C语言是如何实现该结构的。

链表的元素是由结构体来实现struct table *p。结构体中有一个成员是结构体指针struct table *next,而这个结构体指针的类型和此结构体类型相同。除链表最后一个元素外,每一个结构体的指针都指向链表中下一个元素的结构体,最后一个元素的结构体指针为空(NULL)。保存链表时,只需要记录下链表的头指针,即链表中第一个结构体的地址即可。添加一个链表元素时,都需要单独申请一段内存;删除时则将其释放掉。在查找链表时,只需要顺着结构体指针的顺序一个一个往下查找,直到查找的结构体中的成员指针。以下是一个链表结构的示意:

?
1
2
3
4
5
6
struct Student{
int ID;//学号
char[20];//姓名
int marks[5];//5门考试的成绩
struct Student *next;//指向下一个结构体的结构体指针
}

为什么不用结构体数组

有人会有问为什么不直接用一个结构体数组代替链表,结构体数组占据的内存空间是连续的,如果使用malloc指令一样可以动态存储,而且连续的内存空间肯定比不确定的内存空间效果要好。但是如果对一个动态数组插入或者删除元素的话,它后面的所有元素都需要变动位置,因此修改数组会比链表要难的多。但它的优点在于查找方式很灵活,每一个元素相对于数组首元素地址都有一个偏移量i,因此对于数组来说既可以顺向查找也可以反向查找,还可以二分法查找。

由于链表的每一个元素都有一段内存,这些内存未必是连续的,加上链表本身会比结构体数组多一个指向下一个元素的结构体指针,因此从节省内存的角度看链表是不如数组的。但是链表删除元素的时候(假设这个元素是a[k])只需要a[k-1]的next指针指向a[k+1],再把a[k]的内存释放即可,非常方便;插入元素的时候(假设这个元素是a[n]),只需要a[n-1]的next指针指向a[n]再将a[n]的指针指向a[n+1]即可,相比于数组来说只修改了两个元素,速度快而且非常方便。

链表的操作

链表的操作分为——创建表、插入元素、删除元素、清空表、查找表、打印表。其中插入/删除的元素可以是一个也可以说多个。链表从存储类型上来分可以分为静态链表和动态链表,静态链表是事先编写好的链表,占用的内存是静态存储区的内存,使用时不可以对其中的元素进行删减,只能查找;动态链表是按照程序要求生成的链表,存放于动态存储区,结构比较灵活,每一个元素都占据一部分存储空间,如果要删除元素,则释放该位置的内存;如果要添加元素,则申请一个结构体内存区的内存。

创建表

创建链表需要两个指针,一个作为先行指针(*p1),开辟内存并保存结构体的值;一个作为缓存指针(*p2),保留先行指针的所有值并且将它的next指向先行指针。构建链表时,先行指针赋一个值,后行指针保存一个值并且后行指针的next指向先行指针。赋值终止时,先行指针的next指向NULL,同时将先行指针赋值给后行指针,链表即构建完毕
代码窗口可以通过键盘的"←"和"→"查看。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Student * input()
{
    struct Student *p1,*p2,*head=NULL;
    printf("************************动态链表实验***********************\n【输入动态链表】\n");
    printf("请依次输入学号 姓名  身高(cm)  体重(kg)(用空格间隔,学号输入0结束):\n");
    p2=p1=(struct Student *)malloc(LEN);//开辟内存
    scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);
    if(p1->ID==0)return(head);
    else head=p1;
    while(p1->ID!=0)
    {
        p2->next=p1;
        p2=p1;
        p2->BMI=(float)p2->weight/(p2->height/100)/(p2->height/100);//求BMI指数
        p1=(struct Student *)malloc(LEN);//开辟内存
        scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);
    }
    p2->next=NULL;
    return(head);//返回链表头指针
}

删除元素

删除元素链表的第n个元素只需要将第n-1个元素的next指针指向第n+1个元素,再将第n个元素的内存释放即可,我这里是写的其中一个例子,根据关键字学号(int stdID)删除表中的某个元素,同时返回删除后的链表首地址(如果删的是第一个元素,则链表首地址会变)
代码窗口可以通过键盘的"←"和"→"查看。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Student *delate(struct Student *head,int stdID)
{
    struct Student *p1,*p2;
    if(head->ID==stdID)
    {
        p1=head->next;
        free(head);
        return p1;
    }//如果删除的是第一个元素,比较特殊,需要修改头指针,其余不动
    //剩余几种情况都是修改next结构体指针
    for(p1=head;p1!=NULL;p2=p1,p1=p1->next)//p1指针和p2指针同时查找,p1指向当前的学生,p2保指向了上一个学生
    {
        if(p1->ID==stdID)
        {
            p2->next=p1->next;//假设找到了需要删除的学生的学号,则让它上一个学生的指针指向跳过他的下一个学生
            free(p1);
            return head;
        }
    }
    return NULL;//返回NULL代表没找到
}

插入元素

插入元素的原理是,假设要在第n个元素前插入一个元素。首先判断它是不是首元素,如果是,则修改头指针指向该元素,并将该元素的next指向原来的头指针。如果不是首元素,是第k个元素之前插入一个元素,则将第k-1个元素的next指针指向插入元素(或者子表)的地址(或者头指针),将插入元素的next指针(或尾指针)指向第k个元素。本示例代码是根据一个学号(主要关键字)插入一个元素(或者子表)的函数,返回链表的首地址(因为如果在第一个元素前面插入,可能改变链表的首地址)。
代码窗口可以通过键盘的"←"和"→"查看。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Student *insert(struct Student *head,int stdID,struct Student *insertstd)
{
    struct Student *p1,*p2,*p;
    for(p=insertstd;p->next!=NULL;p=p->next);//找到insert链表的最后一个元素
    if(head->ID==stdID)
    {
        p->next=head;
        return insertstd;
    }
 
    for(p1=head;p1!=NULL;p2=p1,p1=p1->next)
    {
        if(p1->ID==stdID)
        {
            p2->next=insertstd;
            p->next=p1;
            return head;
        }
    }
    return NULL;
}

代码及运行结果

完整代码及注释如下:
代码窗口可以通过键盘的"←"和"→"查看。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
#define LEN sizeof(struct Student)//定义结构体变量的大小为符号常量LEN
struct Student{
    int ID;//学号
    char name[20];//姓名
    float height;//身高
    float weight;//体重
    float BMI;//BMI指数,录入时不需要计算
    struct Student *next;//指向下一个结构体
};
struct Student *input();//输入函数
void output(struct Student * head);//输出函数
struct Student *delate(struct Student *head,int stdID);//删除一个元素,返回删除后表的头指针
struct Student *insert(struct Student *head,int stdID,struct Student *insertstd);//返回插入元素(子表)后的头指针
int append(struct Student *head);//插入一个链表,从input函数输入
struct Student *isexist(struct Student *head,int stdID);
int main()
{
    struct Student *present;//当前链表的头指针
    int choice;
    bool next;
    int stdID;
    /*
    1:插入一个元素
    2:删除一个元素
    3:续表
    4:查找表
    */
    printf("**********动态链表实验**********\n初始化一个链表:\n");
    present=input();//当前的链表指针
    do{
        printf("请选择:\n|1:插入元素(子表)\n|2:删除元素\n|3:续表\n|4:查找表\n");
        scanf("%d",&choice);
        switch(choice) 
        {
            case 1:
                printf("请输入插入地点的后一个同学的学号: ");
                scanf("%d",&stdID);
                if(isexist(present,stdID)==NULL)
                {
                    printf("该学生不存在!\n");
                    break;//退出switch语句
                }
                present=insert(present,stdID,input());
                printf("插入元素后的链表为:\n");
                output(present);
                break;
            case 2:
                printf("请输入删除元素的学号:  ");
                scanf("%d",&stdID);
                if(isexist(present,stdID)==NULL)
                {
                    printf("该学生不存在!\n");
                    break;//退出switch语句
                }
                present=delate(present,stdID);
                printf("删除后的链表为:\n");  
                output(present);
                break;
            case 3:
                append(present);
                printf("续表后的链表为:\n");
                output(present);
                break;
            case 4:
                printf("当前链表为:\n");
                output(present);
                break;
        }
        printf("是否继续(Yes:1,No:0):  ");
        scanf("%d",&next);
        fflush(stdin);
    }while(next);
 
     
    return 0;  
}
struct Student * input()
{
    struct Student *p1,*p2,*head=NULL;
    printf("************************动态链表实验***********************\n【输入动态链表】\n");
    printf("请依次输入学号 姓名  身高(cm)  体重(kg)(用空格间隔,学号输入0结束):\n");
    p2=p1=(struct Student *)malloc(LEN);//开辟内存
    scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);
    if(p1->ID==0)return(head);
    else head=p1;
    while(p1->ID!=0)
    {
        p2->next=p1;
        p2=p1;
        p2->BMI=(float)p2->weight/(p2->height/100)/(p2->height/100);//求BMI指数
        p1=(struct Student *)malloc(LEN);//开辟内存
        scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);
    }
    p2->next=NULL;
    return(head);//返回链表头指针
}
void output(struct Student *head)
{
    struct Student *p;
    int num=1;
    p=head;//将头指针地址传给p
    printf("【输出动态链表】\n");
    printf("|学号\t\t|姓名\t|身高\t|体重\t|BMI\n");
    while(p!=NULL)
    {
        printf("%3d|%08d\t|%s\t|%5.2f\t|%5.2f\t|%lf\n",num++,p->ID,p->name,p->height,p->weight,p->BMI);
        p=p->next;
    }
}
struct Student *delate(struct Student *head,int stdID)
{
    struct Student *p1,*p2;
    if(head->ID==stdID)
    {
        p1=head->next;
        free(head);
        return p1;
    }//如果删除的是第一个元素,比较特殊,需要修改头指针,其余不动
    //剩余几种情况都是修改next结构体指针
    for(p1=head;p1!=NULL;p2=p1,p1=p1->next)//p1指针和p2指针同时查找,p1指向当前的学生,p2保指向了上一个学生
    {
        if(p1->ID==stdID)
        {
            p2->next=p1->next;//假设找到了需要删除的学生的学号,则让它上一个学生的指针指向跳过他的下一个学生
            free(p1);
            return head;
        }
    }
    return NULL;//返回NULL代表没找到
}
struct Student *insert(struct Student *head,int stdID,struct Student *insertstd)
{
    struct Student *p1,*p2,*p;
    for(p=insertstd;p->next!=NULL;p=p->next);//找到insert链表的最后一个元素
    if(head->ID==stdID)
    {
        p->next=head;
        return insertstd;
    }
 
    for(p1=head;p1!=NULL;p2=p1,p1=p1->next)
    {
        if(p1->ID==stdID)
        {
            p2->next=insertstd;
            p->next=p1;
            return head;
        }
    }
    return NULL;
}
int append(struct Student *head)//插入一个链表,从input函数输入
{
    struct Student *p;
    for(p=head;p->next!=NULL;p=p->next);//找到head链表的最后一个元素
    p->next=input();//从input输入需要添加的元素,可以是1个或者多个
    return 0;
}
struct Student *isexist(struct Student *head,int stdID)
{
    struct Student *p;
    for(p=head;p!=NULL;p=p->next)
    {
        if(p->ID==stdID)
        {
            return p;
        }
    }
    return NULL;
}

输出效果如下图:

C语言实现线性动态(单向)链表的示例代码

C语言实现线性动态(单向)链表的示例代码

到此这篇关于C语言实现线性动态(单向)链表的示例代码的文章就介绍到这了,更多相关C语言 线性动态(单向)链表内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_44044411/article/details/85233505

延伸 · 阅读

精彩推荐
  • C/C++C语言实现字符串匹配KMP算法

    C语言实现字符串匹配KMP算法

    相信很多人(包括自己)初识KMP算法的时候始终是丈二和尚摸不着头脑,要么完全不知所云,要么看不懂书上的解释,要么自己觉得好像心里了解KMP算法的...

    C语言程序设计7802021-01-29
  • C/C++C语言实现超市管理系统

    C语言实现超市管理系统

    这篇文章主要为大家详细介绍了C语言实现超市管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    水墨ac9862021-09-17
  • C/C++C++之多态(内容不错)

    C++之多态(内容不错)

    什么是多态?顾名思义就是同一个事物在不同场景下的多种形态,需要的朋友可以参考下...

    熊二不二5092021-08-12
  • C/C++C语言位图算法详解

    C语言位图算法详解

    这篇文章主要介绍了C语言实现的位图算法,主要包括了位图算法的定义与应用,对于C程序算法设计的学习有一定的借鉴价值,需要的朋友可以参考下...

    C语言程序设计5062021-01-31
  • C/C++Visual Studio 2019安装、测试创建c语言项目(图文教程)

    Visual Studio 2019安装、测试创建c语言项目(图文教程)

    这篇文章主要介绍了Visual Studio 2019安装、测试创建c语言项目,Visual Studio 2019是完全免费的,而且安装比较简单,现在把安装步骤分享给大家,也给大家做个...

    未来w12462021-08-21
  • C/C++C语言中volatile关键字的作用与使用案例教程

    C语言中volatile关键字的作用与使用案例教程

    这篇文章主要介绍了C语言中volatile关键字的作用与使用案例教程,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是本文的详细内容,需要的朋...

    冀博3572021-11-22
  • C/C++如何正确的使用语句块

    如何正确的使用语句块

    本篇文章是对正确使用语句块进行了详细的分析介绍,需要的朋友参考下...

    C语言教程网5332020-12-03
  • C/C++解析C++中的5个存储类的作用

    解析C++中的5个存储类的作用

    这篇文章主要介绍了C++中的5个存储类的作用,存储类是管理对象的生存期、链接和内存位置的类型说明符,需要的朋友可以参考下...

    飞龙3872021-04-01