C语言双向链表讲解

数据结构 专栏收录该内容
6 篇文章 0 订阅

lfylcj

C语言双向链表讲解

 

 

 

上图对应如下代码:

pnode->prev = pindex->prev;
 pnode->pNext = pindex;
 pindex->prev->pNext = pnode;
 pindex->prev = pnode;

 

 

上图对应如下代码:

pnode->prev = phead;

pnode->pNext = phead->pNext;

phead->pNext->prev = pnode;

phead->pNext = pnode;

 

上图对应如下代码:

pnode->pNext = phead;

 pnode->prev = phead->prev;

 phead->prev->pNext = pnode;

 phead->prev = pnode;

 

 

上图对应如下代码:

pindex->pNext->prev = pindex->prev;

pindex->prev->pNext = pindex->pNext;

 

一、双向链表的概念

    双向链表基于单链表。单链表是单向的,有一个头结点,一个尾结点,要访问任何结点,都必须知道头结点,不能逆着进行。而双链表添加了一个指针域,通过两个指针域,分别指向结点的前结点和后结点。这样的话,可以通过双链表的任何结点,访问到它的前结点和后结点。

    在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点的地址,一般称为右链域;一个存储直接前驱结点地址,一般称之为左链域。

    双向链表结构示意图

    

表头为空,表头的后继节点为"节点10"(数据为10的节点);"节点10"的后继节点是"节点20"(数据为10的节点),"节点20"的前继节点是"节点10";"节点20"的后继节点是"节点30","节点30"的前继节点是"节点20";...;末尾节点的后继节点是表头。

双链表删除节点

删除"节点30"
删除之前:"节点20"的后继节点为"节点30","节点30" 的前继节点为"节点20"。"节点30"的后继节点为"节点40","节点40" 的前继节点为"节点30"。
删除之后:"节点20"的后继节点为"节点40","节点40" 的前继节点为"节点20"。

 

双链表添加节点

在"节点10"与"节点20"之间添加"节点15"
添加之前:"节点10"的后继节点为"节点20","节点20" 的前继节点为"节点10"。
添加之后:"节点10"的后继节点为"节点15","节点15" 的前继节点为"节点10"。"节点15"的后继节点为"节点20","节点20" 的前继节点为"节点15"。

二、C语言实现双向链表

2.1 头文件

复制代码

 1 #pragma once
 2 //新建双向链表。成功返回链表头,否则,返回NULL
 3 extern int create_dLink();
 4 //撤销双向链表,成功返回0,否则返回-1
 5 extern int destory_dLink();
 6 //双向列表是否为空,为空返回1,否则返回0
 7 extern int  is_empty_dLink();
 8 //双向链表的大小
 9 extern int dLink_size();
10 //获取索引index位置的元素,成功返回节点指针,否则返回NULL
11 extern  void* dLink_get(int index);
12 //获取双向链表中第一个元素,成功返回节点指针,否则返回NULL
13 extern void* dLink_getFirst();
14 //获取双向链表中最后一个元素,成功返回节点指针,否则返回NULL
15 extern void* dLink_getTail();
16 /*
17 链表中增加
18 */
19 //在Index位置插值value,成功返回0,否则返回-1;
20 extern int dLink_insert(int index,void * pVal);
21 //在表头插入值
22 extern int dLink_insert_head(void *pVal);
23 //在表尾插入值
24 extern int dLink_insert_tail(void *pVal);
25 /*
26 链表中删除
27 */
28 //在index处删除
29 extern int  dLink_delete(int index);
30 //删除第一个节点
31 extern int dLink_delete_first();
32 //闪电湖第二个节点
33 extern int dLink_delete_tail();

复制代码

2.2 .c

复制代码

  1 #include<stdio.h>
  2 #include "double_link.h"
  3 #include<malloc.h>
  4 
  5 //双向链表节点
  6 typedef struct My_node
  7 {
  8     struct My_node *prev;
  9     struct  My_node *pNext;
 10     void * p;
 11 }my_node;
 12 //b表头不存放元素值
 13 my_node *phead = NULL;
 14 //节点的个数
 15 int node_count = 0;
 16 //创建节点,成功返回节点指针,否则,返回NULL
 17 my_node* create_node(void *pVal)
 18 {
 19     my_node *pnode = NULL;
 20     pnode = (my_node*)malloc(sizeof(My_node));
 21     if (!pnode)
 22     {
 23         printf("create pnode error\n");
 24         return NULL;
 25     }
 26     //默认的,pnode的前一节点和后一节点都指向他自己
 27     pnode->prev  = pnode->pNext = pnode;
 28     //节点的值为pVal
 29     pnode->p = pVal;
 30     return pnode;
 31 }
 32 
 33 //新建双向链表 成功返回0 否则返回-1
 34 int create_dLink()
 35 {
 36     phead = create_node(NULL);
 37     if (!phead)
 38         return -1;
 39     //设置节点的个数
 40     node_count = 0;
 41     return 0;
 42 }
 43 
 44 int destory_dLink()
 45 {
 46     if (!phead)
 47     {
 48         printf("%s failed! dlink is null!\n", __func__);
 49          return -1;
 50     }
 51     My_node*pnode = phead->pNext;
 52     my_node* ptmp = NULL;
 53     if (pnode!=phead)
 54     {
 55         ptmp = pnode;
 56         pnode = pnode->pNext;
 57         free(pnode);
 58     }
 59     free(phead);
 60     phead = NULL;
 61     node_count = 0;
 62     return 0;
 63 }
 64 
 65 int is_empty_dLink()
 66 {
 67     return node_count==0;
 68 }
 69 
 70 int dLink_size()
 71 {
 72     return node_count;
 73 }

 74 //获取双向链表中第Index位置的节点
 75 my_node* get_node(int index)
 76 {
 77     if (index<0 || index >= node_count)
 78     {
 79         printf("%s failed ! index out of bound\n", __func__);
 80         return NULL;
 81     }
 82     //正向查找
 83     if (index <= (node_count / 2))
 84     {
 85         int i = 0;
 86         my_node *pnode = phead->pNext;
 87         while ((i++)<index)
 88         {
 89             pnode = pnode->pNext;
 90         }
 91         return pnode;
 92     }
 93     //反向查找
 94     int j = 0;
 95     int rindex = node_count - index - 1;
 96     my_node *rnode = phead->prev;
 97     while ((j++)<rindex)
 98     {
 99         rnode = rnode->prev;
100     }
101     return rnode;
102 }
103 void * dLink_get(int index)
104 {
105     my_node *pindex = get_node(index);
106     if (!pindex)
107     {
108         printf("%s failed!\n", __func__);
109         return NULL;
110     }
111     return pindex->p;
112 }
113 
114 //获取第一个节点
115 void * dLink_getFirst()
116 {
117     return get_node(0) ;
118 }
119 //获取最后一个节点
120 void * dLink_getTail()
121 {
122     return get_node(node_count-1);
123 }

124 //将值插入到index位置,成功返回0;否则 返回-1
125 int dLink_insert(int index, void * pVal)
126 {
127     //插入表头
128     if (index == 0)
129         return dLink_insert_head(pVal);
130     //获取要插入位置对应的节点
131     my_node* pindex = get_node(index);
132     if (!pindex)
133         return -1;
134     //创建节点
135     my_node* pnode = create_node(pVal);
136     if (!pnode)
137         return -1;
138     pnode->prev = pindex->prev;
139     pnode->pNext = pindex;
140     pindex->prev->pNext = pnode;
141     pindex->prev = pnode;
142     node_count++;
143     return 0;
144 }
145 //数值插入表头
146 int dLink_insert_head(void * pVal)
147 {
148     my_node* pnode = create_node(pVal);
149     if (!pnode)
150         return -1;
151     pnode->prev = phead;
152     pnode->pNext = phead->pNext;
153     
154     phead->pNext->prev = pnode;
155     phead->pNext = pnode;
156     node_count++;
157     return 0;
158 }
159 
160 int dLink_insert_tail(void * pVal)
161 {
162     my_node* pnode = create_node(pVal);
163     if (!pnode)
164         return -1;
165     pnode->pNext = phead;
166     pnode->prev = phead->prev;
167     phead->prev->pNext = pnode;
168     phead->prev = pnode;
169     return 0;
170 }
171 
172 int dLink_delete(int index)
173 {
174     my_node* pindex = get_node(index);
175     if (!pindex)
176     {
177         printf("%s failed! the index in out of bound\n",__func__);
178         return -1;
179     }
180     pindex->pNext->prev = pindex->prev;
181     pindex->prev->pNext = pindex->pNext;
182     free(pindex);
183     node_count--;
184     return 0;
185 }
186 
187 int dLink_delete_first()
188 {
189     return dLink_delete(0);
190 }
191 
192 int dLink_delete_tail()
193 {
194     return dLink_delete(node_count-1);
195 }

复制代码

2.3 test测试代码

复制代码

 1 #include<stdio.h>
 2 #include"double_link.h"
 3 //1.双向链表操作数为int
 4 void int_test()
 5 {
 6     int arr[10] = {11,55,67,90,21,45,23,59,79,10};
 7     printf("xxxxxxxxxxxxxxxxx\n");
 8     create_dLink();                    //创建链表
 9     dLink_insert(0, &arr[0]);        //双向链表表头插入
10     dLink_insert(0, &arr[1]);        //双向链表表头插入
11     dLink_insert(0, &arr[2]);        //双向链表表头插入
12     dLink_insert(0, &arr[3]);        //双向链表表头插入
13     dLink_insert(0, &arr[4]);        //双向链表表头插入
14     dLink_insert(0, &arr[5]);        //双向链表表头插入
15     printf("is_empty_dLink()=%d\n",is_empty_dLink());    //双向链表是否为空
16     printf("dLink_size()=%d\n", dLink_size());                    //双向链表的大小
17     //遍历双向链表
18     int i ;
19     int * p ;
20     int sz = dLink_size();
21     for ( i = 0; i < sz; i++)
22     {
23         p = (int*)dLink_get(i);
24         printf("dLink_get(%d)=%d\n",i,*p);
25     }
26     destory_dLink();
27 }
28 
29 //2.操作数为字符串
30 void string_test()
31 {
32     char* str[] = {"one","two","three","four","five"};
33     create_dLink();                    //创建链表
34     dLink_insert(0, str[0]);        //双向链表表头插入
35     dLink_insert(0, str[1]);        //双向链表表头插入
36     dLink_insert(0, str[2]);        //双向链表表头插入
37     printf("is_empty_dLink()=%d\n", is_empty_dLink());    //双向链表是否为空
38     printf("dLink_size()=%d\n", dLink_size());                    //双向链表的大小
39                                                                 //遍历双向链表
40     int i ;
41     char * p ;
42     int sz = dLink_size();
43     for (i = 0; i < sz; i++)
44     {
45         p = (char*)dLink_get(i);
46         printf("dLink_get(%d)=%s\n", i, p);
47     }
48     destory_dLink();
49 }
50 //3.双向链表为结构体
51 typedef struct MyStruct
52 {
53     int id;
54     char name[20];
55 } stu;
56 stu arr_stu[] =
57 {
58     {1000,"lii"},
59     { 1001,"mike" },
60     { 1002,"lucky" },
61     { 1003,"eric" },
62 };
63 #define arr_stu_size  ((sizeof(arr_stu))/(sizeof(arr_stu[0])))
64 void stuc_test()
65 {
66     create_dLink();                    //创建链表
67     dLink_insert(0, &arr_stu[0]);        //双向链表表头插入
68     dLink_insert(0, &arr_stu[1]);        //双向链表表头插入
69     dLink_insert(0, &arr_stu[2]);        //双向链表表头插入
70     printf("is_empty_dLink()=%d\n", is_empty_dLink());    //双向链表是否为空
71     printf("dLink_size()=%d\n", dLink_size());                    //双向链表的大小
72                                                                 //遍历双向链表
73     int i ;
74     stu * p ;
75     int sz = dLink_size();
76     for (i = 0; i < sz; i++)
77     {
78         p = (stu*)dLink_get(i);
79         printf("dLink_get(%d)=[%d,%s]\n", i, p->id,p->name);
80     }
81     destory_dLink();
82 }
83 int main()
84 {
85     int_test();
86     string_test();
87     stuc_test();
88     
89     return 0;
90 }

复制代码

2.34结果显示

  • 3
    点赞
  • 0
    评论
  • 20
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) C语言的双链表,可以增加。插入,删除,修改,销毁等函数接口。
©️2022 CSDN 皮肤主题:创作都市 设计师:CSDN官方博客 返回首页

打赏作者

卖火柴的小男孩2020

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值