数据结构之线性表(七)——双向链表

为什么要用双向链表?

  • 因为单链表中的结点有指示后继结点的指针域,找后继结点方便。即,查找某结点后继结点的复杂度为 O ( 1 ) O(1) O(1)
  • 但是在单链表中,没有指示前驱结点的指针域,找前驱结点难,要从表头出发查找。即,查找某结点前驱结点的复杂度为 O ( n ) O(n) O(n)

双向链表可以克服这种缺点。

双向链表的定义与特点

1.定义

双向链表:在单链表中的每个结点里在增加一个指向其前驱结点的指针域prior,这样链表就形成了有两个方向不同的链。

可定义如下,

typedef struct DuLNode
{
	ElemType data;
	struct DuLNode *prior, *next;
}DuLNode, *DuLinkList;

在这里插入图片描述
在这里插入图片描述

双向循环链表:让头结点的前驱指针指向链表的最后一个结点,让最后一个结点的后继指针指向头结点。
在这里插入图片描述

2.特点

对称性:当前结点的前驱结点的next域和当前结点的后继结点的prior域都是指向当前结点的指针。
即,p->prior->next = p = p->next->prior
在这里插入图片描述
双向链表中的某些操作,如求表长,查找某个元素等,这些操作只涉及一个方向上的指针,所以它们的算法与单链表同。但插入、删除时,则需要同时修改两个方向上的指针,两者的复杂度均为 O ( n ) O(n) O(n)

双向链表的插入操作

如下图,在结点p之前插入值为x的结点s,
在这里插入图片描述

  • 具体步骤

    • 1.找到第i个结点p(查找算法与单链表同),并且新建结点s,将s的数据域赋x。
    • 2.将a所在结点变成x的前驱结点。即,为结点x的prior域赋a结点的地址(p->prior)。在这里插入图片描述
    • 3.结点a的next域赋结点x的地址,结点a的next域表示为p->prior->next
      在这里插入图片描述
    • 4.结点x的next域赋结点b的地址。
      在这里插入图片描述
    • 5.结点p的priort域赋结点x的地址。
      在这里插入图片描述
  • 算法描述

void ListInsert_DuL(DuLinkList& L, int i, ElemType e)
{
	if (!p = GetElemP_DuL(L, i))
		return ERROR;
	s = new DuLNode;
	s->data = e;
	s->prior = p->prior;
	p->prior->next = s;
	s->next = p;
	p->prior = s;
	return OK;
}

双向链表的删除操作

如下图,要删除链表中的b结点,当前指针p指向要删除的结点b
在这里插入图片描述

  • 具体步骤

    • 1.结点c变成结点a的后继,即结点的a的next域指向c结点。
      结点a的next域为:p->prior->next
      c结点的地址:p->next
      在这里插入图片描述
    • 2.结点a变成结点c的前驱,即结点的c的prior域指向a结点。
      结点c的prior域为:p->next->prior
      a结点的地址:p->prior
      在这里插入图片描述
    • 3.删除结点b。
  • 算法描述

void ListDelete_DuL(DuLinkList& L, int i, ElemType& e)
{
	if (!p = GetElemP_DuL(L, i))
		return ERROR;
	e = p->data;
	p->prior->next = p->next;
	p->next->prior = p->prior;
	delete p;
	return OK;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值