前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >双向链表

双向链表

作者头像
猿人谷
发布2018-01-17 11:20:35
1K0
发布2018-01-17 11:20:35
举报
文章被收录于专栏:猿人谷猿人谷猿人谷

双向链表

      在线性链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针往后寻查其他结点。若要寻查结点的直接前趋,则需从表头指针出 发。换句话说,在单链表中,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 }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2013-10-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档