面試考察頻率:⭐⭐⭐⭐
什麼是雙向鏈表?
在單向鏈表的前提下,增加了前驅指針,可以跟輕鬆的訪問一個節點的前驅與後繼節點
雙向鏈表優缺點?
優點:可以更便捷的訪問一個節點的前驅與後繼。
缺點:需要佔用更多一些的內存。刪除節點操作變得更加複雜。
如何來實現?
構建思路如下
當前節點的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);
}
}
}
}