C语言之双向链表
一、双向链表
在上一篇文章我们已经介绍了单向链表,了解了其具体构造及其基本操作,我们也能发现其部分缺点,查找数据和删除数据的时候都必须从链表头指针开始挨个遍历,直到找到目标节点。
双向链表的双向指的的就是我们遍历链表的时候可以正序遍历也可逆序遍历,链表内的节点不止与下一个节点有关系,与上一个节点同样有关系。具有两个方向。
1.1双向链表的构造如下图所示
根据图我们可以看到,双向链表和单向链表一样拥有数据域和指针域,但是区别在于双向链表的指针域有两个指针,分别为next和prev。其中next是指向下一个节点的指针,prev是指向上一个节点的指针。
二、双向链表的基本操作
首先我们来看看双向链表元素的数据结构
[code]typedef struct _Link_t { int data; //节点保存的数据 struct _Link_t *next; //指向下一个节点的指针 struct _Link_t *prev; //指向上一个节点的指针 } LINK_T;
对比单向链表的数据结构,双向链表元素结构与单向链表不同点在于其多了一个用于指向上一个节点的指针成员。
2.1创建节点
[code]LINK_T *Create_Node(int data) { LINK_T *node; node = (LINK_T*)malloc(sizeof(LINK_T)); memset(node, 0, sizeof(LINK_T)); node->next = NULL; node->prev = NULL; node->data = data; return node; }
该部分内容与单向链表基本一致。
2.2链表插入
从链表头插入如下图所示
在这里我们需要注意一个细节,就是头指针后面有没有节点。
假设头指针后面没有节点,则插入新节点后,新节点的下一个节点的prev指针就不用指向新节点,没有下一个节点。
假设头指针后面有节点,则插入新节点后,新节点的下一个节点的prev指针就需要指向新节点。
下面我们看看实际代码:
[code]void Insert_Top(LINK_T *head, LINK_T *node) { LINK_T *p = head; node->next = p->next; //首先新节点的next指针指向头指针中next的指向 if (NULL != p->next) //判断头指针后面还有没有节点 p->next->prev = node; //如果有的话,新节点的下一个节点的prev指针指向新节点 node->prev = p; //新节点的prev指针指向头指针 p->next = node; //头指针的next指向新节点 }
链表尾部插入节点:
由图分析,我们可知双向链表尾部插入操作与单向链表从尾部插入无异,只需要注意加上prev指针的指向即可。
具体代码如下所示:
[code]void Insert_Tail(LINK_T *head, LINK_T *node) { LINK_T *p = head; while((p->next != NULL) && (p = p->next)); //找到尾部 node->prev = p; p->next = node; }
2.3节点的遍历
正序遍历代码如下:
[code]void Traverse_P(LINK_T *head) { LINK_T *p = head; do { printf("%d ", p->data); } while((NULL != p->next) && (p = p->next)); printf("\n"); }
逆序遍历代码如下:
[code]void Traverse_R(LINK_T *head) { LINK_T *p = head; while((NULL != p->next) && (p = p->next)); do { printf("%d ", p->data); } while((NULL != p ->prev) && (p = p->prev)); printf("\n"); }
该程序是找到链表最后一个节点再去反向遍历。
三、基本例程
例程功能实现,先将数组data的数据按序存到每个节点中,然后正序遍历并且打印数据,再逆序遍历并且打印数据以验证各调用函数的正确性。
在该程序中,头指针的数据域的数据代表链表的节点数。
[code]#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct _Link_t { int data; struct _Link_t *next; struct _Link_t *prev; } LINK_T; LINK_T *Create_Node(int data);//创建节点 void Insert_Tail(LINK_T *head, LINK_T *node);//尾插入节点 void Insert_Top(LINK_T *head, LINK_T *node);//头插入节点 void Traverse_P(LINK_T *head);//正序遍历 void Traverse_R(LINK_T *head);//逆序遍历 int main(int argc, const char *argv[]) { int i = 0; int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; LINK_T *head; head = (LINK_T*)malloc(sizeof(LINK_T)); memset(head, 0, sizeof(LINK_T)); head->prev = NULL; for (i = 0; i < 10; i++) { Insert_Top(head, Create_Node(data[i])); head->data++; } Traverse_P(head); Traverse_R(head); return 0; } LINK_T *Create_Node(int data) { LINK_T *node; node = (LINK_T*)malloc(sizeof(LINK_T)); memset(node, 0, sizeof(LINK_T)); node->next = NULL; node->prev = NULL; node->data = data; return node; } void Insert_Tail(LINK_T *head, LINK_T *node) { LINK_T *p = head; while((p->next != NULL) && (p = p->next)); node->prev = p; p->next = node; } void Insert_Top(LINK_T *head, LINK_T *node) { LINK_T *p = head; node->next = p->next; if (NULL != p->next) p->next->prev = node; node->prev = p; p->next = node; } void Traverse_P(LINK_T *head) { LINK_T *p = head; do { printf("%d ", p->data); } while((NULL != p->next) && (p = p->next)); printf("\n"); } void Traverse_R(LINK_T *head) { LINK_T *p = head; while((NULL != p->next) && (p = p->next)); do { printf("%d ", p->data); } while((NULL != p ->prev) && (p = p->prev)); printf("\n"); }
程序执行结果如下:
[code]10 10 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10 10
仓促成文,不当之处,尚祈方家和读者批评指正。联系邮箱1772348223@qq.com
- 点赞
- 收藏
- 分享
- 文章举报
- C++异常处理机制
- C++调用Pytorch的坑:遇到free(): invalid pointer:
- 《计算方法与实习》 c语言与python分别实现二分法求方程的根
- C语言课程设计
- c语言个人信息管理系统
- c语言贪吃蛇游戏(可运行)控制台界面
- 【C/C++】随机数的生成
- 【C++】string::find函数
- 【C++】string::substr函数
- 【C/C++】关于strstr函数和c_str()函数
- 文件转C语言数组 二进制文件转16进制 BIN转HEX BMP图片转数组 任意文件转数组 软件下载
- Windows下C++使用MinGW编译protocol buffer
- 【C语言】变量
- 【C语言】堆栈区的生长方向
- 探讨C语言字符串的初始化,赋值以及会出现的问题
- 关于指针与数组的关系(C语言)
- 使用C语言实现哈夫曼树的编码,压缩和解码过程。
- C语言小白上楼梯问题(递归)
- C语言小白用xcode遇见的问题,求大牛们相助
- 关于C语言递归函数的心得及一些例题