目录

文章目录

  • 目录
  • 双向链表
  • 双向链表结点的数据结构
  • 双向链表的操作集合
  • 应用示例
    • 创建双向链表
    • 清理双向链表
    • 查询链表结点
    • 更新链表结点的数据
    • 插入链表结点
    • 删除结点
    • 打印链表数据

双向链表

双向链表(Double linked list)是在操作系统中常用的数据结构,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱,其结点组成如下:

双向链表,所谓 “双向” 值其支持 “顺序和逆序” 查找,如下图:

所以双向链表也常被称之为环(Ring),常用于作为网络数据报文的内存缓存空间,因为双向链表具有两个特征:

  1. 支持顺序和逆序查找,肯定能把数据报文遍历完。
  2. 成环状,即:内存空间固定,不会因为数据报文的多少而造成内存越界。

双向链表的缺点是不支持按某个值或区间的快速查找,也不支持数据的快速插入,所以不十分适用于需求灵活的逻辑控制场景(e.g. 数据库)。

双向链表结点的数据结构

typedef int data_type;               // 4Btypedef struct dlist_node_s {data_type data;                   // 4B + 4B(填充)struct dlist_node_s* prior;    // 8Bstruct dlist_node_s* next; // 8B
} dlist_node_t;

双向链表的操作集合

  • dlist_create:(构造函数)创建双向链表
  • dlist_find:查询链表结点
  • dlist_change:更新链表结点的数据
  • dlist_insert:插入链表结点
  • dlist_delete:删除链表结点
  • dlist_print_int:输入链表结点的数据
  • dlist_distory:(析构函数)清理双向链表

应用示例

#include <stdio.h>
#include <stdlib.h>#define DL_LEN 5typedef int data_type;             // 4Btypedef struct dlist_node_s {data_type data;                   // 4B + 4B(填充)struct dlist_node_s* prior;    // 8Bstruct dlist_node_s* next; // 8B
} dlist_node_t;enum {SUCCESS,FAIL
};int list[DL_LEN] = {5, 2, 0, 13, 14};static int dlist_create(dlist_node_t** dlist)
{printf("dlist_create.\n");*dlist = (dlist_node_t*)malloc(sizeof(dlist_node_t));if (NULL == *dlist) {printf("Allocation dlist_node_t failed.\n");return FAIL;}(*dlist)->prior = NULL;(*dlist)->next = NULL;(*dlist)->data = list[0];/* 开始,链表的第一个结点 head 就是链表本身,后续再不断增加新的结点。 */dlist_node_t* head = *dlist;for (int i=1; i<DL_LEN; i++) {/* 初始化新的结点。 */dlist_node_t* new_node = (dlist_node_t*)malloc(sizeof(dlist_node_t));new_node->next = NULL;new_node->prior = head;    // 新结点的 prior 为 headnew_node->data = list[i];head->next = new_node;     // head 的 next 为新结点/* 头指针向后移动一个结点,循环添加直到双向链表的所有结点都构造完成。 */head = head->next;}return SUCCESS;
}static void dlist_distory(dlist_node_t* dlist)
{dlist_node_t* temp = dlist;while (temp) {temp = temp->next;if (NULL != temp) {free(temp->prior);}}
}static int dlist_find(dlist_node_t* dlist, int* idx, data_type find_data)
{printf("dlist_find: %d.\n", find_data);int pos = 0;    // positiondlist_node_t* temp = dlist;while (temp) {if (find_data == temp->data) {idx = &pos;return SUCCESS;} else {temp = temp->next;pos++;}}printf("Data %d not exist.\n", find_data);return FAIL;
}static int dlist_change(dlist_node_t* dlist, int idx, data_type new_data)
{printf("dlist_change, index: %d, new_data: %d.\n", idx, new_data);if (idx > DL_LEN) {printf("Position out of bounds.\n");return FAIL;}/* 使用 temp 来移动链表位置,不能直接移动 dlist,dlist 始终执行链表的起始位置,即索引为 [0] 的结点。 */dlist_node_t* temp = dlist;/* 移动到预期的 idx 结点。 */for (int i=1; i<idx; i++) {temp = temp->next;}/* 更新。 */temp->data = new_data;return SUCCESS;
}static int dlist_insert(dlist_node_t* dlist, int idx, data_type insert_data)
{printf("dlist_insert, index: %d, insert_data: %d\n", idx, insert_data);/* 构造一个新的结点。 */dlist_node_t* new_node = (dlist_node_t*)malloc(sizeof(dlist_node_t));new_node->next = NULL;new_node->prior = NULL;new_node->data = insert_data;if (idx > (DL_LEN + 1)) {printf("Position out of bounds.\n");    // 新结点的位置不连续return FAIL;}/* 头部插入。 */if (1 == idx) {dlist->prior = new_node;    // 步骤 1new_node->next = dlist;     // 步骤 2dlist = new_node;           // 步骤 3} else {dlist_node_t* temp = dlist;for (int i=1; i<idx; i++) {temp = temp->next;}/* 中间插入。 */if (temp->next != NULL) {new_node->next = temp->next;    // 步骤 1new_node->prior = temp;         // 步骤 2temp->next->prior = new_node;   // 步骤 3temp->next = new_node;          // 步骤 4} else {temp->next = new_node;    // 步骤 1new_node->prior = temp;   // 步骤 2}}return SUCCESS;
}static int dlist_delete(dlist_node_t* dlist, data_type del_data) {printf("dlist_delete, del_data: %d\n", del_data);dlist_node_t* temp = dlist;while (temp) {if (del_data == temp->data) {temp->next->prior = temp->prior;temp->prior->next = temp->next;free(temp);return SUCCESS;} else {temp = temp->next;}}printf("dlist_delete not exist.\n");return FAIL;
}static void dlist_print_int(dlist_node_t* dlist)
{dlist_node_t* temp = dlist;int idx = 0;while (temp) {printf("index: %d - data: %d\n", idx, temp->data);temp = temp->next;idx++;}
}int main(void)
{/*** 这里不能直接定义使用双重指针 **dlist,* 因为这样会导致 *dlist 的类型没有定义,从而无法进行下一步的 malloc 内存分配,* 应该:*   1. dlist_node_t* dlist;*   2. &dlist 入参*   3. 在函数内再使用 dlist_node_t** dlist 变量*/dlist_node_t* dlist;if (FAIL == dlist_create(&dlist)) {printf("dlist_create ERROR.\n");exit(1);}dlist_print_int(dlist);int idx = 0;if (FAIL == dlist_find(dlist, &idx, 13)) {printf("dlist_find ERROR.\n");exit(1);}dlist_print_int(dlist);if (FAIL == dlist_change(dlist, 1, 666)) {printf("dlist_change ERROR.\n");exit(1);}dlist_print_int(dlist);if (FAIL == dlist_insert(dlist, 2, 2020)) {printf("dlist_insert ERROR.\n");exit(1);}dlist_print_int(dlist);if(FAIL == dlist_delete(dlist, 13)) {printf("dlist_delete ERROR.\n");exit(1);}dlist_print_int(dlist);dlist_distory(dlist);return 0;
}

创建双向链表

创建一个双向链表:5,2,0,13,14。

static int dlist_create(dlist_node_t** dlist)
{printf("dlist_create.\n");*dlist = (dlist_node_t*)malloc(sizeof(dlist_node_t));if (NULL == *dlist) {printf("Allocation dlist_node_t failed.\n");return FAIL;}(*dlist)->prior = NULL;(*dlist)->next = NULL;(*dlist)->data = list[0];/* 开始,链表的第一个结点 head 就是链表本身,后续再不断增加新的结点。 */dlist_node_t* head = *dlist;for (int i=1; i<DL_LEN; i++) {/* 初始化新的结点。 */dlist_node_t* new_node = (dlist_node_t*)malloc(sizeof(dlist_node_t));new_node->next = NULL;new_node->prior = head;    // 新结点的 prior 为 headnew_node->data = list[i];head->next = new_node;     // head 的 next 为新结点/* 头指针向后移动一个结点,循环添加直到双向链表的所有结点都构造完成。 */head = head->next;}return SUCCESS;
}

清理双向链表

static void dlist_distory(dlist_node_t* dlist)
{dlist_node_t* temp = dlist;while (temp) {temp = temp->next;if (NULL != temp) {free(temp->prior);}}
}

查询链表结点

双向链表不支持快速查询,所以只能循环遍历匹配查询。匹配成功后,返回结点的索引号。

static int dlist_find(dlist_node_t* dlist, int* idx, data_type find_data)
{printf("dlist_find: %d.\n", find_data);int pos = 0;    // positiondlist_node_t* temp = dlist;while (temp) {if (find_data == temp->data) {idx = &pos;return SUCCESS;} else {temp = temp->next;pos++;}}printf("Data %d not exist.\n", find_data);return FAIL;
}

更新链表结点的数据

static int dlist_change(dlist_node_t* dlist, int idx, data_type new_data)
{printf("dlist_change, index: %d, new_data: %d.\n", idx, new_data);if (idx > DL_LEN) {printf("Position out of bounds.\n");return FAIL;}/* 使用 temp 来移动链表位置,不能直接移动 dlist,dlist 始终执行链表的起始位置,即索引为 [0] 的结点。 */dlist_node_t* temp = dlist;/* 移动到预期的 idx 结点。 */for (int i=1; i<idx; i++) {temp = temp->next;}/* 更新。 */temp->data = new_data;return SUCCESS;
}

插入链表结点

插入一个链表的节点有 3 种情况:

  1. 头部插入
  2. 尾部插入
  3. 中间插入
static int dlist_insert(dlist_node_t* dlist, int idx, data_type insert_data)
{printf("dlist_insert, index: %d, insert_data: %d\n", idx, insert_data);/* 构造一个新的结点。 */dlist_node_t* new_node = (dlist_node_t*)malloc(sizeof(dlist_node_t));new_node->next = NULL;new_node->prior = NULL;new_node->data = insert_data;if (idx > (DL_LEN + 1)) {printf("Position out of bounds.\n");    // 新结点的位置不连续return FAIL;}/* 头部插入。 */if (1 == idx) {dlist->prior = new_node;    // 步骤 1new_node->next = dlist;     // 步骤 2dlist = new_node;           // 步骤 3} else {dlist_node_t* temp = dlist;for (int i=1; i<idx; i++) {temp = temp->next;}/* 中间插入。 */if (temp->next != NULL) {new_node->next = temp->next;    // 步骤 1new_node->prior = temp;         // 步骤 2temp->next->prior = new_node;   // 步骤 3temp->next = new_node;          // 步骤 4} else {temp->next = new_node;    // 步骤 1new_node->prior = temp;   // 步骤 2}}return SUCCESS;
}

删除结点

tatic int dlist_delete(dlist_node_t* dlist, data_type del_data) {printf("dlist_delete, del_data: %d\n", del_data);dlist_node_t* temp = dlist;while (temp) {if (del_data == temp->data) {temp->next->prior = temp->prior;temp->prior->next = temp->next;free(temp);return SUCCESS;} else {temp = temp->next;}}printf("dlist_delete not exist.\n");return FAIL;
}

打印链表数据


static void dlist_print_int(dlist_node_t* dlist)
{dlist_node_t* temp = dlist;int idx = 0;while (temp) {printf("index: %d - data: %d\n", idx, temp->data);temp = temp->next;idx++;}
}

数据结构 — 双向链表相关推荐

  1. 数据结构 —— 双向链表(超详细图解 接口函数实现)

    系列文章目录 数据结构 -- 顺序表 数据结构 -- 单链表 数据结构 -- 双向链表 数据结构 -- 队列 数据结构 -- 栈 数据结构 -- 堆 数据结构 -- 二叉树 数据结构 -- 八大排序 ...

  2. python 双向链表_数据结构-双向链表(Python实现)

    数据结构在编程世界中一直是非常重要的一环,不管是开发还是算法,哪怕是单纯为了面试,数据结构都是必修课,今天我们介绍链表中的一种--双向链表的代码实现. 好了,话不多说直接上代码. 双向链表 首先,我们 ...

  3. python程序实现双向链表_数据结构-双向链表(Python实现)

    数据结构在编程世界中一直是非常重要的一环,不管是开发还是算法,哪怕是单纯为了面试,数据结构都是必修课,今天我们介绍链表中的一种--双向链表的代码实现. 好了,话不多说直接上代码. 双向链表 首先,我们 ...

  4. Linux c 算法与数据结构--双向链表

    链表是linux c中非常重要的数据结构,双向链表与单向链表的区别,是它每个节点有两个指针域,分别指向该节点的前一个节点与后一个节点: 而链表的操作主要是查询.插入.删除.遍历等,下面来看一个双向链表 ...

  5. C语言数据结构双向链表之温故而知新

    单向链表:http://blog.csdn.net/morixinguan/article/details/77756216 单向链表理解了,那双向就非常简单了,没什么好说的,看图: 双链表的引入是为 ...

  6. 数据结构 | 双向链表

    一.数据结构定义 /* 链表结点 */ typedef int ListType; typedef struct node {ListType data; // 存放整型数据struct node* ...

  7. Linux内核分析--内核中的数据结构双向链表续【转】

    在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_H ...

  8. 数据结构-双向链表的实现

    题目: 1 双向升序链表结构定义如下: 2 class Node { 3 4 private int value;// 保存节点的数据 5 6 private Node pre, next;// 保存 ...

  9. 数据结构--双向链表

    双向链表的一种Go语言实现 package mainimport "fmt"//定义节点信息 type dNode struct {id intname stringpre *dN ...

最新文章

  1. OpenCV(项目)车牌识别2 -- 车牌字符分割(直方图)
  2. java取number长度_Java中常用方法(NumberMath)
  3. 多继承、经典类与新式类、新式类的C3算法详解
  4. python学习--关注容易被忽略的知识点--(四)函数式编程
  5. mysql建外键失败
  6. LUOGU P4281 [AHOI2008]紧急集合 / 聚会 (lca)
  7. 背包九讲-第三讲 多重背包
  8. 【时间序列分析】03.正态时间序列与严平稳序列
  9. orangepizero编译ch934x驱动
  10. 地震波形自动分类及识别(构想)
  11. Qt之打印颜色字体设置
  12. java 宝箱概率问题
  13. 花了10分钟,终于明白矩阵的逆到底有什么用
  14. echarts关系图配置详解
  15. JavaScript制作简易聊天窗口
  16. Qt 字符编码转换(UTF-8 转换为 GBK) \u7528\u6237\u672a\u7b7e\u7ea6
  17. 单片机 c语言 p1控制流水灯,单片机控制的流水灯程序
  18. 我创业的这一年 九个月只有两个人
  19. 设计模式(四)~结构型模式(2)
  20. 研究人员发现火星存在大量水冰积层,且距离地表只有几米

热门文章

  1. Nmap扫描教程之Nmap基础知识
  2. 两台服务器安装redis集群_Redis Cluster搭建高可用Redis服务器集群
  3. linspace python_python np.linspace
  4. ios开发中计算代码运算时间_iOS日历、日期、时间的计算
  5. python的x 2是什么意思_python中startx是什么意思
  6. php的const,php中const入门
  7. #数据集#:并发脑电图、心电图和多剂量经颅电刺激行为的数据集
  8. 用于视力恢复的脑机接口综述(一)(修改)
  9. Android中获取资源文件的几种方法
  10. JAVA实现斐波那契数列问题(《剑指offer》)