双向链表

简介: 双向链表       在线性链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针往后寻查其他结点。若要寻查结点的直接前趋,则需从表头指针出 发。换句话说,在单链表中,NextElem的执行时间是o(1),而PriorElem的执行时间为O(n)。

双向链表

      在线性链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针往后寻查其他结点。若要寻查结点的直接前趋,则需从表头指针出 发。换句话说,在单链表中,NextElem的执行时间是o(1),而PriorElem的执行时间为O(n)。为克服单链表这种单向性的缺点,可利用双 向链表。

       双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。

//线性表的双向链表存储结构
typedef struct DulNode
{
    ElemType data;
    struct DulNode *prior;  //直接前驱指针
    struct DulNode *next;   //直接后继指针
}DulNode , *DuLinkList;

      双向链表既然是比单链表多了如可以反向遍历查找等数据结构,那么也就需要付出一些小的代价:在插入和删除时,需要更改两个指针变量。

插入操作

     插入操作,其实并不复杂,不过顺序很重要,千万不能写反了。假设存储元素e的结点s,要实现将结点s插入到结点p和p->next之间需要下面几步,如下图所示。

 

s->prior = p;           //把p赋值给s的前驱,如图中1所示
s->next = p->next;      //把p->next赋值给s的后继,如图中2所示
p->next->prior = s;     //把s赋值给p->next的前驱,如图中3所示
p->next = s;            //把s赋值给p的后继,如图中4所示

      关键在于他们的顺序,由于第2步和第3步都用到了p->next。如果第4步先执行,则会使得p->next提前变成了s,使得插入的工作玩不成。所以不妨把上面这种图在理解的基础上记忆,顺序是先搞定s的前驱和后继,再搞定后结点的前驱,最后解决前结点的后继

删除操作

      如要删除结点p,只需要下面两个步骤,如下图所示。

p->prior->next = p->next;       //把p->next赋值给p->prior的后继,如图中1所示
p->next->prior = p->prior;      //把p->prior赋值给p->next的前驱,如图中2所示
free(p);                        //释放结点

 

双链循环线性表的表示与实现代码:

  1 //双链循环线性表的表示与实现
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 
  5 /******************************************************************************
  6 /* 数据类型和常量定义
  7 /******************************************************************************/
  8 #define TURE         1
  9 #define FALSE        0
 10 #define OK           1
 11 #define ERROR        0
 12 #define OVERFLOW    -2
 13 
 14 typedef int Status;
 15 typedef int ElemType;
 16 
 17 /******************************************************************************
 18 /* 数据结构声明
 19 /******************************************************************************/
 20 /* 线性表的双向链表存储结构 */
 21 typedef struct DuLNode {
 22     ElemType data;
 23     struct DuLNode *prior;
 24     struct DuLNode *next;
 25 } DuLNode, *DuLinkList;
 26 
 27 //初始化双链循环线性表
 28 Status InitList_DuL(DuLinkList &L) {
 29     L = (DuLinkList)malloc(sizeof(DuLNode));
 30     L->next = L->prior = L;
 31     return OK;
 32 }
 33 
 34 //获取双链循环线性表中的元素
 35 DuLNode * GetElemP_Dul(DuLinkList L, int i) {
 36     int j;    struct DuLNode *p = L;
 37     if (i < 1)   //非法i值
 38         return NULL;
 39     if (p->next == L) //空双向循环链表
 40         return L;
 41     p = L->next; j = 1;   //初始化, p指向第一个结点, j为计数器
 42     while(p != L && j < i) {  //顺指针向后查找, 直到p指向第i个元素或p指向头结点
 43         p = p->next; ++j;
 44     }
 45     return p;
 46 }
 47 
 48 //插入元素
 49 Status ListInsert_DuL(DuLinkList &L, int i, ElemType e) {
 50     //在带头结点的双链循环线性表L中第i个位置之前插入元素e
 51     //i的合法值为1 <= i <= 表长 + 1
 52     struct DuLNode *p = NULL;
 53     struct DuLNode *s = NULL;
 54     if (!(p = GetElemP_Dul(L, i)))
 55         return ERROR;
 56     if(!(s = (DuLinkList)malloc(sizeof(DuLNode)))) return ERROR;
 57     s->data = e;
 58     s->prior = p->prior; p->prior->next = s;
 59     s->next = p;         p->prior = s;
 60     return OK;
 61 }
 62 
 63 //删除元素
 64 Status ListDelete_DuL(DuLinkList &L, int i, ElemType &e) {
 65     //在带头结点的双链线性表L中, 删除第i个元素, 并由e返回其值
 66     //i的合法值为1 <= i <= 表长
 67     struct DuLNode *p = NULL;
 68     if (!(p = GetElemP_Dul(L, i)) || L == GetElemP_Dul(L, i))
 69         return ERROR;
 70     e = p->data;
 71     p->prior->next = p->next;
 72     p->next->prior = p->prior;
 73     free(p); return OK;
 74 }
 75 
 76 //遍历线性表
 77 Status ListTraverse_DuL(DuLinkList &L, Status (*Visit)(ElemType)) {
 78     printf("traverse list: ");
 79     struct DuLNode *p = L->next; //略过头结点
 80     while (p != L) {
 81         Visit(p->data);
 82         p = p->next;
 83     }
 84     return OK;
 85 }
 86 
 87 //访问线性表中的元素
 88 Status Visit(ElemType e)
 89 {
 90     printf("%d ", e);
 91     return OK;
 92 }
 93 
 94 //测试函数
 95 void main()
 96 {
 97     DuLinkList L;  ElemType e;
 98     InitList_DuL(L);
 99     
100     //插入元素
101     if (OK == ListInsert_DuL(L, 0, 55)) printf("insert 55 succeed!\n");
102     if (OK == ListInsert_DuL(L, 1, 56)) printf("insert 56 succeed!\n");
103     ListTraverse_DuL(L, Visit); printf("\n");
104     if (OK == ListInsert_DuL(L, 2, 57)) printf("insert 57 succeed!\n");
105     if (OK == ListInsert_DuL(L, 1, 58)) printf("insert 58 succeed!\n");
106     ListTraverse_DuL(L, Visit); printf("\n");
107 
108     //删除元素
109     if (OK == ListDelete_DuL(L, 1, e)) printf("the %dst elem deleted!\n", 1);
110     if (OK == ListDelete_DuL(L, 3, e)) printf("the %drd elem deleted!\n", 3);
111     if (OK == ListDelete_DuL(L, 4, e)) 
112         printf("the %dth elem deleted!\n", 4);
113     else
114         printf("delete the %dth elem failed!\n", 4);
115     if (OK == ListDelete_DuL(L, 1, e)) printf("the %dst elem deleted!\n", 1);
116     ListTraverse_DuL(L, Visit); printf("\n");
117     if (OK == ListDelete_DuL(L, 1, e)) printf("the %dst elem deleted!\n", 1);
118     ListTraverse_DuL(L, Visit); printf("\n");
119 }

 

 

 

 


微信公众号: 猿人谷
如果您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】
如果您希望与我交流互动,欢迎关注微信公众号
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

相关文章
|
3月前
|
Linux iOS开发 MacOS
硬盘分区怎么做?这几款分区工具新手也能轻松上手
本文介绍了五款实用的硬盘分区工具,满足不同用户需求。Windows用户可使用内置的磁盘管理器或DiskPart命令行工具,简单易上手;DiskGenius功能全面,适合进阶用户进行复杂操作和数据恢复;Mac用户可借助Disk Utility完成基本磁盘管理任务;Linux用户及高级玩家可选择开源工具GParted,支持多种文件系统并具备高度自由度。根据自身需求和技术水平选择合适的工具,可高效完成硬盘分区与管理。
|
5月前
|
存储 JavaScript 前端开发
基于 ant-design-vue 和 Vue 3 封装的功能强大的表格组件
VTable 是一个基于 ant-design-vue 和 Vue 3 的多功能表格组件,支持列自定义、排序、本地化存储、行选择等功能。它继承了 Ant-Design-Vue Table 的所有特性并加以扩展,提供开箱即用的高性能体验。示例包括基础表格、可选择表格和自定义列渲染等。
406 6
|
7月前
|
并行计算 安全 算法
量子计算在密码学中的应用与挑战:解密未来的安全
量子计算在密码学中的应用与挑战:解密未来的安全
375 6
|
10月前
|
数据采集 监控 算法
大数据与物流行业:智能配送的实现
【10月更文挑战第31天】在数字化时代,大数据成为物流行业转型升级的关键驱动力。本文探讨大数据如何在物流行业中实现智能配送,包括数据采集与整合、数据分析与挖掘、智能配送规划及实时监控与评估,通过案例分析展示了大数据在优化配送路线和提升物流效率方面的巨大潜力,展望了未来智能配送的高度自动化、实时性和协同化趋势。
1008 1
|
10月前
|
分布式计算 监控 大数据
如何优化Spark中的shuffle操作?
【10月更文挑战第18天】
|
12月前
|
Linux Docker 容器
Docker操作 :容器命令
Docker操作 (四)
329 56
|
存储 运维 分布式计算
云计算的优势与未来发展趋势
一、前言 随着科技的不断发展,企业在信息技术应用方面遇到了许多挑战,如成本高昂、设备更新换代困难、运维复杂等。为了解决这些问题,越来越多的企业开始关注云计算技术,并逐步实现数字化转型。本文将深入探讨云计算的基础概念、企业采用云计算的优势、行业应用案例,以及未来发展趋势与挑战,希望能帮助读者全面了解云计算的前景与潜力。 二、云计算的基础概念
3987 0
|
11月前
(7)Qt中的自定义槽(函数)
这篇文章介绍了在Qt中如何定义和使用自定义槽函数,包括类成员函数、静态类成员函数、全局函数和lambda表达式作为槽函数的示例,以及使用lambda表达式时的注意事项。
352 2
(7)Qt中的自定义槽(函数)
|
存储 SQL 关系型数据库
技术好文:TiDB架构及设计实现
技术好文:TiDB架构及设计实现
985 0
|
安全 数据安全/隐私保护
一些常见的ip代理协议的类型有哪些?以及它们的特点?
代理服务器作为客户端和目标服务器间的中介,遵循HTTP、HTTPS、SOCKS5等协议。HTTP协议简单直接,HTTPS提供加密和身份验证,而SOCKS5更底层,采用二进制请求,提供更强的安全性。在性能和安全优先的情况下,SOCKS5是首选。

热门文章

最新文章