【C语言】高效数据存储和检索:C语言链表的应用

目录

一、链表的概述

二、数组和链表的优缺点

三、静态链表的设计

四、typedef 给结构体类型取别名

五、以学生管理系统为例详解链表

1、main测试函数

2、link.h头文件声明

3、实现操作函数link.c

4、实现查询链表操作函数

5、删除链表指定节点

6、释放链表

7、翻转链表

8、链表按学号排序

六、双向循环链表

1、概述

2、 节点设计

 3、双向循环链表实例


一、链表的概述

        链表是数据元素在物理存储上非连续,通过链表中的指针链接次序实现逻辑上连续的一种线性存储结构。 

        链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成( malloc)

每个节点包括两个部分:

一个是存储数据元素的数据域
另一个是存储下一个节点地址的指针域

保存头节点地址方式:

带头的链表:用一个空数据的节点来存储,用其指针域保存头节点地址。

 不带头的链表:定义一个4B的指针变量存放头节点地址。

二、数组和链表的优缺点

数组优点:遍历元素效率高

数组缺点:

静态数组:int arr[N]; 必须事先确定数组元素的个数,过多浪费 过小容易溢出,删除插入 数据效率低(需要移动大量的数据

动态数组:不需要事先知道元素的个数,在使用中动态申请,删除插入数据效率低(需要移 动大量的数据

链表优点:不需要事先知道数据的个数,在使用中动态申请,插入删除不需要移动数据

链表缺点:遍历效率低

三、静态链表的设计

以四个学生学号姓名为例:

#include <stdio.h>
struct stu
{
	//数据域
	int num;
	char name[100];
	//指针域
	struct stu* next;
};
void test()
{
	struct stu node1 = { 1,"lucy",NULL };
	struct stu node2 = { 2,"bob",NULL };
	struct stu node3 = { 3,"张三",NULL };
	struct stu node4 = { 4,"李四",NULL };
	//定义链表头
	struct stu* head = &node1;
	//连接链表
	node1.next = &node2;
	node2.next = &node3;
	node3.next = &node4;
	//遍历链表
	struct stu * pb = &head;//重新定义另外一个指针变量,防止指向错误导致链表头丢失
	while (pb != NULL)
	{
		printf("%d %s\n", pb->num, pb->name);
		pb移动到下一个节点位置
		pb=pb->next;
	}
}
int main(int argc, char const* argv[])
{
	test();
	return 0;
}

四、typedef 给结构体类型取别名

方式1:

typedef struct data_long
{
	int a;
	short b;
}DATA;//DATA是类型
void test()
{
	struct data_long ob1;
	DATA ob2;//用别名定义
}

方式2:

struct data_long
{
	int a;
	short b;
};
void test()
{
	typedef struct data_long DATA;
	DATA ob2;//用别名定义
}

结构体指针别名:

typedef struct data_long
{
	int a;
	short b;
}DATA,*DATA_P;
void test()
{
	//DATA==struct data_long
	DATA A = {10,20};//用别名初始化
	//DATA_P==struct data_long *
	DATA_P p = &A;
	printf("%d %d", p->a, p->b);
}

五、以学生管理系统为例详解链表

        该系统包含了链表插入,链表遍历,链表查询删除链表指定节点,释放链表等五种链表常规操作。为方便观看,链表查询删除链表指定节点,释放链表等的实现操作函数单独列出。

1、main测试函数

#include <stdio.h>
#include <string.h>
#include"link.h>
STU *head=NULL;//链表头设置为NULL(链表存在依据)
void help(void)
{
    printf("*******************************\n");
    printf("*help:帮助信息 *\n");
    printf("*insert:插入链表节点 *\n");
    printf("*print:遍历链表节点 *\n");
    printf("*search:查询链表某个节点 *\n");
    printf("*delete:删除链表某个节点 *\n");
    printf("*free:释放整个链表 *\n");
    printf("*quit:退出程序 *\n");
    printf("*reverse:翻转链表 *\n");
    printf("*sort:链表排序 *\n");
    printf("*******************************\n");
}
int main(int argc, char const *argv[])
{
    help();
    while (1)
{
    char cmd[128] = "";//定义commend命令
    printf("请输入操作命令:");
    scanf("%s", cmd);
if (strcmp(cmd, "help") == 0)
{
    help();
}
else if (strcmp(cmd, "insert") == 0)
{
    printf("‐‐‐‐‐‐‐‐‐链表插入‐‐‐‐‐‐‐‐\n");
    printf("请输入需要插入的学生信息num name score:");
    STU tmp;
    scanf("%d %s %f",&temp.num,tmp.name,tmp.score);
    //将tmp插入到链表中
    head=insert_link(head,tmp);//返回值更新head指向
}
lse if (strcmp(cmd, "print") == 0)
{
    printf("‐‐‐‐‐‐‐‐‐链表遍历‐‐‐‐‐‐‐‐\n");
    print_link(head);
}
else if (strcmp(cmd, "search") == 0)
{
    printf("‐‐‐‐‐‐‐‐‐链表查询‐‐‐‐‐‐‐‐\n");
    printf("请输入要查找的姓名:");
    char name[32]="";
    scanf("%s\n",name);
    STU *ret=search_link(head,name);
    if(ret!=NULL)
    {
       printf("查询的结果:%d %s %f\n",ret->num,ret->name,ret->score);
    }
}
else if (strcmp(cmd, "delete") == 0)
{
    printf("‐‐‐‐‐‐‐‐‐删除链表指定节点‐‐‐‐‐‐‐‐\n");
    printf("请输入要删除的学号:");
    scanf("%d",num);
    head=delete_link(head,num);
}
else if (strcmp(cmd, "free") == 0)
{
    printf("‐‐‐‐‐‐‐‐‐释放链表‐‐‐‐‐‐‐‐\n");
    head=free_link(head);
}
else if (strcmp(cmd, "quit") == 0)
{
    head=free_link(head);
    break;
}
else if(strcmp(cmd,"reserve")==0)
{
    head=reverse_link(head);
}
else if(strcmp(cmd,"aort")==0)
{
    sort_link(head);
}
}
    return 0;
}

2、link.h头文件声明

链表节点类型设计:

//防止头文件重复包含
#ifndef _LINK_H_
#ifdef _LINK_H_
//定义链表节点类型
typedef struct stu
{
    //数据域
    int num;
    char name[30];
    float score;
    //指针域
    struct stu *next;
}STU;
//声明
extern STU *insert_link(STU *head, STU tmp);
extern void print_link(STU *head);
extern STU *search_link(STU *head,char *name);
extern STU *delete_link(STU *head,int num);
extern STU* free_link(STU* head)
extern STU* reverse_link(STU *head)
extern void sort_link(STU *head)
#endif

3、实现操作函数link.c

#include<stdio.h>//perror等头文件
#include<stdlib.h>//malloc,exit头文件
#include<string.h>
#include"link.h"
#if 1 //链表头部插入
STU* insert_link(STU *head, STU tmp)
{
    //为待插入的数据申请空间
    STU *pi=(STU *)calloc(1,sizeof(STU));
    if(pi=NULL)
    {
        perror("calloc");//打印错误信息
        exit(-1);
    }
    //tmp数据赋值到*pi
    *pi=tmp;
    pi->next=NULL;
    //判断链表是否存在
    if(head=NULL)
    {
        head=pi;
    else //存在
    {
        pi->next=head;
        head=pi;
    }
    return head;    
}
#endif
#if 0  //链表尾部插入
STU* insert_link(STU *head, STU tmp)
{
    //为待插入的数据申请空间
    STU *pi=(STU *)calloc(1,sizeof(STU));
    if(pi=NULL)
    {
        perror("calloc");//打印错误信息
        exit(-1);
    }
    //tmp数据赋值到*pi
    *pi=tmp;
    pi->next=NULL;
    //判断链表是否存在
    if(head=NULL)
    {
        head=pi;
    else //存在
    {
        STU *pb=head;
        while(pb!=NULL)
        {
            pb=pb->next;
        }
        pb->next=pi;
    }
    return head;    
}
#endif
#if 0  //按学号有序插入(包含头部和尾部插入)
STU* insert_link(STU *head, STU tmp)
{
    //为待插入的数据申请空间
    STU *pi=(STU *)calloc(1,sizeof(STU));
    if(pi=NULL)
    {
        perror("calloc");//打印错误信息
        exit(-1);
    }
    //tmp数据赋值到*pi
    *pi=tmp;
    pi->next=NULL;
    //判断链表是否存在
    if(head=NULL)
    {
        head=pi;
    else //存在
    {
        //寻找插入点位置
        STU *pb=head,*pf=head;
        while((pb‐>num < pi‐>num) && (pb‐>next != NULL))
        {
            pf=pb;
            pb=pb->next;
        }
        if(pb‐>num >= pi‐>num)//头部,中部插入
        {
            if(head=pi)
            {
                pi->next=head;
                head=pi
            }
            else //中部插入
            {
                pf->next=pi;
                pi->next=pb;
                }
        else
        {
            pb‐>next = pi;
            
        }
    }
    return head;    
}
#endif
void print_link(STU *head)
{
    //判断链表是否存在
    if(head=NULL)
    {
    printf("link not exits");
    return;
    }
    else
    {
        STU *pb=head;
        while(pb!=NULL)
        {
            printf("%d %s %f\n",pb->num,pb->name,pb->score);
            pb=pb->next;
        }
    }
    return;
}
    

4、实现查询链表操作函数

STU *search_link(STU *head, char *name)
{
    //判断链表是否存在
    if (NULL == head)
    {
        printf("link not exist\n");
        return NULL;
    }
    else //链表存在
    {
        STU *pb = head;
        while ((strcmp(pb‐>name, name) != 0) && (pb‐>next != NULL))
        {
            pb = pb‐>next;
        }
//找到
        if (strcmp(pb‐>name, name) == 0)
        {
            return pb;
        }
    }
    printf("未找到相关数据\n");
    return NULL;
}

5、删除链表指定节点

STU* delete_link(STU *head, int num)
{
    //判断链表是否存在
    if(NULL == head)
    {
        printf("link not exist\n");
        return head;
    }
    else
    {
        //查找删除的点
        STU *pb=head, *pf = head;
        while((pb‐>num != num)&&(pb‐>next != NULL))
            {
                pf = pb;
                pb = pb‐>next;
            }
        if(pb‐>num == num)//找到
        {
            //判断删除点的位置
            if(pb == head)//删除头节点
            {
                head=head‐>next;
                //free(pb);
            }
            else//删除中尾部节点
            {
                pf‐>next = pb‐>next;
                //free(pb);
            }
            free(pb);
            printf("已成功删除num=%d的节点\n", num);
        }
        else//未找到
        {
            printf("未找到需要删除的节点\n");
        }
    }
return head;
}

6、释放链表

STU* free_link(STU* head)
{
    //判断链表是否存在
    if(NULL == head)
    {
        printf("link not exist\n");
    }
    else
    {
        STU *pb = head;
        while(pb!=NULL)
        {
            head = pb‐>next;
            free(pb);
            pb = head;
        }
    }
return NULL;
}

7、翻转链表

STU* reverse_link(STU *head)
{
    //判断链表是否存在
    if(NULL == head)
    {
        printf("link not exist\n");
        //return head;
    }
    else
    {
        STU *pb = head‐>next;
        STU *pn = NULL;
        head‐>next = NULL;
        while(pb != NULL)
        {
            //纪录pb下一个节点位置
            pn = pb‐>next;
            //pb连接上一个节点
            pb‐>next = head;
            head = pb;
            pb = pn;
        }
        printf("链表翻转成功\n");
    }
return head;
}

8、链表按学号排序

void sort_link(STU *head)
{
    //判断链表是否存在
    if (NULL == head)
    {
        printf("link not exist\n");
        return;
    }
    else
    {
        STU *p_i = head, *p_j = head; //int i=0,j=0;
        while (p_i‐>next != NULL) //for(i=0;i<n‐1;i++)
        {
            STU *p_min = p_i; //int min = i;
            p_j = p_min‐>next; //j=min+1;
            while (p_j != NULL) //for(;j<n;j++)
             {
                 if (p_min‐>num > p_j‐>num) //if(arr[min] > arr[j])
                 p_min = p_j; //min = j;
                p_j = p_j‐>next; //j++
             }
            if (p_i != p_min) //if(i != min)
            {
                //交换数据
                STU tmp = *p_i;
                *p_i = *p_min;
                *p_min = tmp;
                tmp.next = p_i‐>next;
                 p_i‐>next = p_min‐>next;
                 p_min‐>next = tmp.next;
            }
            p_i = p_i‐>next; //i++
        }
    }
return;
}

六、双向循环链表

1、概述

2、 节点设计

typedef struct stu
{
    //数据域
    int num;
    char name[32];
    //指针域
    struct stu *pre;
    struct stu *next;
}STU;

 3、双向循环链表实例

        用双向链表设计一个学生信息系统,包含插入、遍历、查找、删除、释放等功能。

1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 typedef struct stu
5 {
6 //数据域
7 int num;
8 char name[32];
9
10 //指针域
11 struct stu *pre;
12 struct stu *next;
13 } STU;
14 STU *head = NULL;
15 void insert_link(STU **p_head, STU tmp);
16 void print_link(STU *head);
17 STU *search_link(STU *head, int num);
18 void delete_link(STU **p_head, int num);
19 void free_link(STU **p_head);
20 int main(int argc, char const *argv[])
21 {
22 int n = 0;
23 printf("请输入学生的个数:");
24 scanf("%d", &n);
25
26 int i = 0;
27 for (i = 0; i < n; i++)
28 {
29 printf("请输入第%d个学员的信息:", i + 1);
30 STU tmp;
31 scanf("%d %s", &tmp.num, tmp.name);
32
33 insert_link(&head, tmp);
34 }
35
36 //遍历链表
37 print_link(head);
38
39 //查询
40 printf("请输入你要查询的学号:");
41 int num = 0;
42 scanf("%d", &num);
43 STU *ret = search_link(head, num);
44 if (ret != NULL)
45 {
46 printf("查询的结果:%d %s\n", ret‐>num, ret‐>name);
47 }
48
49 //删除指定节点
50 printf("请输入你要删除的学号:");
51 scanf("%d", &num);
52
53 delete_link(&head, num);
54
55 //遍历链表
56 print_link(head);
57
58 //释放这个链表
59 free_link(&head);
60
61 //遍历链表
62 print_link(head);
63
64 return 0;
65 }
66
67 //尾插法
68 void insert_link(STU **p_head, STU tmp)
69 {
70 STU *head = *p_head;
71
72 //为插入的节点申请空间
73 STU *pi = (STU *)calloc(1, sizeof(STU));
74 *pi = tmp;
75 pi‐>next = NULL;
76 pi‐>pre = NULL;
77
78 //判断链表是否为空
79 if (NULL == head)
80 {
81 head = pi;
82 pi‐>next = pi;
83 pi‐>pre = pi;
84 }
85 else
86 {
87 head‐>pre‐>next = pi;
88 pi‐>next = head;
89 pi‐>pre = head‐>pre;
90 head‐>pre = pi;
91 }
92
93 //更新外部的head
94 *p_head = head;
95 }
96
97 void print_link(STU *head)
98 {
99 //判断链表是否存在
100 if (NULL == head)
101 {
102 printf("link not exist\n");
103 return;
104 }
105 else
106 {
107 STU *pn = head;
108 STU *pr = head‐>pre;
109
110 while (1)
111 {
112 if (pn == pr) //链表节点为奇数个
113 {
114 printf("%d %s\n", pn‐>num, pn‐>name);
115 break;
116 }
117 else if (pn‐>next == pr) 链表节点为偶数个
118 {
119 printf("%d %s\n", pn‐>num, pn‐>name);
120 printf("%d %s\n", pr‐>num, pr‐>name);
121 break;
122 }
123 else
124 {
125 printf("%d %s\n", pn‐>num, pn‐>name);
126 printf("%d %s\n", pr‐>num, pr‐>name);
127 pn = pn‐>next;
128 pr = pr‐>pre;
129 }
130 }
131 }
132 return;
133 }
134
135 STU *search_link(STU *head, int num)
136 {
137 //判断链表是否存在
138 if (NULL == head)
139 {
140 printf("link not exist\n");
141 return NULL;
142 }
143 else
144 {
145 STU *pn = head;
146 STU *pr = head‐>pre;
147
148 while ((pn‐>num != num) && (pr‐
>num != num) && (pn != pr) && (pn‐>next != pr))
149 {
150 pn = pn‐>next;
151 pr = pr‐>pre;
152 }
153
154 if (pn‐>num == num)
155 {
156 return pn;
157 }
158 else if (pr‐>num == num)
159 {
160 return pr;
161 }
162 else
163 {
164 printf("没有找到相关节点\n");
165 }
166 }
167
168 return NULL;
169 }
170
171 #if 0
172 void delete_link(STU **p_head, int num)
173 {
174 STU *head = *p_head;
175
176 if (NULL == head)
177 {
178 printf("link not exist\n");
179 return;
180 }
181 else
182 {
183 STU *pn = head;
184 STU *pr = head‐>pre;
185
186 while ((pn‐>num != num) && (pr‐
>num != num) && (pn != pr) && (pn‐>next != pr))
187 {
188 pn = pn‐>next;
189 pr = pr‐>pre;
190 }
191
192 if (pn‐>num == num) //头部、中部节点
193 {
194 if (pn == head) //删除头节点
195 {
196 head‐>next‐>pre = head‐>pre;
197 head‐>pre‐>next = head‐>next;
198 head = head‐>next;
199 //free(pn);
200 }
201 else //删除中部节点
202 {
203 pn‐>pre‐>next = pn‐>next;
204 pn‐>next‐>pre = pn‐>pre;
205 //free(pn);
206 }
207 printf("成功删除节点:%d %s\n", pn‐>num, pn‐>name);
208 free(pn);
209 }
210 else if (pr‐>num == num) //尾部、中部
211 {
212 pr‐>pre‐>next = pr‐>next;
213 pr‐>next‐>pre = pr‐>pre;
214 printf("成功删除节点:%d %s\n", pr‐>num, pr‐>name);
215 free(pr);
216 }
217 else
218 {
219 printf("没有找到相关节点\n");
220 }
221 }
222
223 *p_head = head;
224 }
225 #endif
226
227 #if 1
228 void delete_link(STU **p_head, int num)
229 {
230 STU *head = *p_head;
231
232 if (NULL == head)
233 {
234 printf("link not exist\n");
235 return;
236 }
237 else
238 {
239 STU *pn = head;
240 STU *pr = head‐>pre;
241
242 while ((pn‐>num != num) && (pr‐
>num != num) && (pn != pr) && (pn‐>next != pr))
243 {
244 pn = pn‐>next;
245 pr = pr‐>pre;
246 }
247
248 if (pn‐>num == num) //头部、中部节点
249 {
250 pn‐>next‐>pre = pn‐>pre;
251 pn‐>pre‐>next = pn‐>next;
252 if (pn == head) //删除头节点
253 {
254 head = head‐>next;
255 }
256
257 printf("成功删除节点:%d %s\n", pn‐>num, pn‐>name);
258 free(pn);
259 }
260 else if (pr‐>num == num) //尾部、中部
261 {
262 pr‐>pre‐>next = pr‐>next;
263 pr‐>next‐>pre = pr‐>pre;
264 printf("成功删除节点:%d %s\n", pr‐>num, pr‐>name);
265 free(pr);
266 }
267 else
268 {
269 printf("没有找到相关节点\n");
270 }
271 }
272
273 *p_head = head;
274 }
275 #endif
276
277 void free_link(STU **p_head)
278 {
279 STU *head = *p_head;
280
281 if (NULL == head)
282 {
283 printf("link not exist\n");
284 return;
285 }
286 else
287 {
288 STU *pn = head;
289 do
290 {
291 head = head‐>next;
292 free(pn);
293 pn = head;
294 } while (pn != (*p_head));
295 }
296
297 *p_head = NULL;
298 return;
299 }

猜你喜欢

转载自blog.csdn.net/m0_75045191/article/details/131729008