面试考察频率:⭐⭐⭐⭐
什么是双向链表?
在单向链表的前提下,增加了前驱指针,可以跟轻松的访问一个节点的前驱与后继节点
双向链表优缺点?
优点:可以更便捷的访问一个节点的前驱与后继。
缺点:需要占用更多一些的内存。删除节点操作变得更加复杂。
如何来实现?
构建思路如下
当前节点的next指向下一个节点。当前节点的pre指向上一个节点如此循环。(注:头节点的pre始终指向最后一个节点,为了方便遍历,具体的在后面会讲)。基本的样子就入下图所示(绘制粗糙0.0)
基础结构表示:
public class LinkedListNode
{
public LinkedListNode pre;
public LinkedListNode next;
public int val;
}
初始化:
private void InitList(int c, LinkedListNode node)
{
node = new LinkedListNode();
node.pre = node;
node.next = null;
node.val = -1;
}
就是创建一个头节点,该节点不进行数据记录只是为了方便操作。初始只有一个节点时pre要指向自己。
尾插入:
public void Add_Back(LinkedListNode node, int x)
{
LinkedListNode tmp = node;
while (tmp.next != null)
{
tmp = tmp.next;
}
LinkedListNode p = new LinkedListNode();
p.next = null;
p.pre = tmp;
p.val = x;
node.pre = p;
tmp.next = p;
m_length++;
}
尾插入和单链表的尾插入基本相似,都要先遍历找到最后一个节点。然后记得更新头节点的pre指针,始终指向最后一个节点。
打印与反向打印:
public void PrintList(LinkedListNode node)
{
LinkedListNode p = node;
while (p.next != null)
{
Console.WriteLine(p.next.val);
p = p.next;
}
Console.WriteLine();
}
public void ReversePrintList(LinkedListNode node)
{
LinkedListNode p = node.pre;
while (p != node)
{
Console.WriteLine(p.val);
p = p.pre;
}
Console.WriteLine();
}
正向打印没有什么多说的,和单链表的打印一样。
反向打印从头节点开始,因为头节点的pre始终指向最后一个节点,所以也就是相当与从最后一个节点开始遍历一样,逐个走每个节点的pre域。当遍历的pre域等于头节点时表示遍历完成。
删除节点:
public void DeleteValueAsPosition(LinkedListNode node, int pos)
{
LinkedListNode tNode= FindNodeAsPosition(node, pos);
if(pos==Length)//如果是尾节点的情况
{
Console.WriteLine("删除的是尾节点");
node.pre = tNode.pre;
tNode.pre.next = null;
}
else//如果不是尾巴节点的情况
{
tNode.pre.next = tNode.next;
tNode.next.pre = tNode.pre;
}
m_length--;
}
需要考虑是最后一个节点还是非最后一个节点。
如果为最后一个节点要更新头节点的pre,然后取到倒数第二个节点,把next与最后一个节点断开。
如果不是最后一个节点要取得到要删除节点的上一个节点,设置其next域断开当前节点。然后取到要删除节点的下一个节点,设置其pre域断开当前节点。
完整代码如下,更多数据C#数据结构源码欢迎浏览我的仓库:https://github.com/w199753/DataStructural-CSharp 最后附上完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataStructural
{
public class DoubleLinkedList
{
private int m_length = 0;
public int Length { get { return m_length; } }
public class LinkedListNode
{
public LinkedListNode pre;
public LinkedListNode next;
public int val;
}
public void CreateList(int c, LinkedListNode node)
{
InitList(c, node);
for (int i = 0; i < c; i++)
{
int x = int.Parse(Console.ReadLine().ToString());
Add_Back(node, x);
}
}
private void InitList(int c, LinkedListNode node)
{
node = new LinkedListNode();
node.pre = node;
node.next = null;
node.val = -1;
}
/// <summary>
/// 尾插入
/// </summary>
/// <param name="node"></param>
/// <param name="x"></param>
public void Add_Back(LinkedListNode node, int x)
{
LinkedListNode tmp = node;
while (tmp.next != null)
{
tmp = tmp.next;
}
LinkedListNode p = new LinkedListNode();
p.next = null;
p.pre = tmp;//设置新节点的pre指向最后一个节点
p.val = x;
node.pre = p;//设置头节点的pre域为新节点
tmp.next = p;//连接新节点
m_length++;
}
/// <summary>
/// 插入元素,要判断实在尾部还是在身子部
/// </summary>
/// <param name="node"></param>
/// <param name="pos"></param>
/// <param name="x"></param>
public void Insert(LinkedListNode node, int pos, int x)
{
LinkedListNode p = new LinkedListNode();
if(pos==Length-1)
{
Add_Back(node, x);
}
else
{
LinkedListNode tNode = FindNodeAsPosition(node, pos);
p.val = x;
p.pre = tNode;
p.next = tNode.next;
tNode.next.pre = p;
tNode.next = p;
}
m_length++;
}
public void PrintList(LinkedListNode node)
{
LinkedListNode p = node;
while (p.next != null)
{
Console.WriteLine(p.next.val);
p = p.next;
}
Console.WriteLine();
}
public void ReversePrintList(LinkedListNode node)
{
LinkedListNode p = node.pre;
while (p != node)
{
Console.WriteLine(p.val);
p = p.pre;
}
Console.WriteLine();
}
public LinkedListNode FindNodeAsPosition(LinkedListNode node, int pos)
{
LinkedListNode tNode = node;
int tPos = 1;
while (tNode.next != null)
{
if (tPos == pos)
{
Console.WriteLine("value is:" + tNode.next.val);
return tNode.next;
}
tNode = tNode.next;
tPos++;
}
return null;
}
public LinkedListNode FindNodeAsValue(LinkedListNode node, int val)
{
LinkedListNode tNode = node;
while (tNode.next != null)
{
if (tNode.next.val == val) return tNode.next;
tNode = tNode.next;
}
return null;
}
public int IndexOf(LinkedListNode node,int val)
{
int index = -1;
int z = 0;
LinkedListNode tNode = node;
while (tNode.next != null)
{
if (tNode.next.val == val)
{
index = z;
return index;
}
tNode = tNode.next;
z++;
}
return index;
}
/// <summary>
/// 删除节点,要考虑最后一个和中间元素的情况。
/// </summary>
/// <param name="node"></param>
/// <param name="pos"></param>
public void DeleteValueAsPosition(LinkedListNode node, int pos)
{
LinkedListNode tNode= FindNodeAsPosition(node, pos);
if(pos==Length)//如果是尾节点的情况
{
Console.WriteLine("删除的是尾节点");
node.pre = tNode.pre;
tNode.pre.next = null;
}
else//如果不是尾巴节点的情况
{
tNode.pre.next = tNode.next;
tNode.next.pre = tNode.pre;
}
m_length--;
}
public void DeleteValueAsValue(LinkedListNode node,int value)
{
LinkedListNode tNode = FindNodeAsValue(node, value);
int index = IndexOf(node, value);
if (index==Length-1)//最后一个节点
{
node.pre = tNode.pre;
tNode.pre.next = null;
}
else//不是最后一个节点
{
tNode.pre.next = tNode.next;
tNode.next.pre = tNode.pre;
}
m_length--;
}
/// <summary>
/// 清空所有节点
/// </summary>
/// <param name="node"></param>
public void Clear(LinkedListNode node)
{
int len = Length;
for (int i=1;i<=len;i++)
{
DeleteValueAsPosition(node, 1);
}
}
}
}