双向链表

双向链表是一种链表结构,每个结点包含prev和next引用,分别指向直接前驱和后继结点,使得查找前后结点的时间复杂度均为O(1)。在双向链表中,插入和删除操作比单链表更复杂,涉及到更多的引用更新。此外,循环链表是双向链表的一种特殊情况,最后一个结点的next引用指向头结点,形成一个循环结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面介绍的单链表允许从一个结点直接访问它的后继结点,所以, 找直接后继结点的时间复杂度是O(1)。但是,要找某个结点的直接前驱结点,只能从表的头引用开始遍历各结点。如果某个结点的Next等于该结点,那么,这个结点就是该结点的直接前驱结点。也就是说,找直接前驱结点的时间复杂度是O(n),n是单链表的长度。当然,我们也可以在结点的引用域中保存直接前驱结点的地址而不是直接后继结点的地址。这样,找直接前驱结点的时间复杂度只有O(1),但找直接后继结点的时间复杂度是O(n)。如果希望找直接前驱结点和直接后继结点的时间复杂度都是O(1),那么,需要在结点中设两个引用域,一个保存直接前驱结点的地址,叫prev,一个直接后继结点的地址,叫next,这样的链表就是双向链表(Doubly Linked List)
这里写图片描述双向链表的结点结构示意图
双向链表结点的定义与单链表的结点的定义很相似,,只是双向链表多了一个字段prev。

public class DbNode<T>
{
private T data; //数据域
private DbNode<T> prev; //前驱引用域
private DbNode<T> next; //后继引用域
//构造器
public DbNode(T val, DbNode<T> p)
{
data = val;
next = p;
}
//构造器
public DbNode(DbNode<T> p)
{
next = p;
}
//构造器
public DbNode(T val)
{
data = val;
next = null;
}
//构造器
public DbNode()
{
data = default(T);
next = null;
}
//数据域属性
public T Data
{
get
{
return data;
}
set
{
data = value;
}
}
//前驱引用域属性
public DbNode<T> Prev
{
get
{
return prev;
}
set
{
prev = value;
}
}
//后继引用域属性
public DbNode<T> Next
{
get
{
return next;
}
set
{
next = value;
}
}
}

由于双向链表的结点有两个引用,所以,在双向链表中插入和删除结点比单链表要复杂。双向链表中结点的插入分为在结点之前插入和在结点之后插入,插入操作要对四个引用进行操作.
设p是指向双向链表中的某一结点,即p存储的是该结点的地址,现要将一个结点s插入到结点p的后面,
插入过程如图
这里写图片描述
操作如下:
➀ p.Next.Prev = s;
➁ s.Prev = p;
➂ s.Next = p.Next;
➃ p.Next = s;

引用域值的操作的顺序不是唯一的,但也不是任意的,操作➂必须放到操作➃的前面完成,否则p直接后继结点的就找不到了.

双向链表中结点的删除
以在结点之后删除为例来说明在双向链表中删除结点的情况。设p是指向双向链表中的某一结点,即p存储的是该结点的地址,现要将一个结点P删除
操作如下:
➀ p.Prev.Next = P.Next;
➁ p.Next.Prev = p.Prev;
这里写图片描述

循环链表
有些应用不需要链表中有明显的头尾结点。在这种情况下,可能需要方便地从最后一个结点访问到第一个结点。此时,最后一个结点的引用域不是空引用,而是保存的第一个结点的地址(如果该链表带结点,则保存的是头结点的地址),也就是头引用的值。带头结点的循环链表
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值