一文探索C#中實(shí)現(xiàn)雙向鏈表的方法
一、涉及到的知識(shí)點(diǎn)
1.定義
在雙向鏈表中,每個(gè)節(jié)點(diǎn)有兩個(gè)指針域,一個(gè)指向它的前一個(gè)節(jié)點(diǎn)(即直接前驅(qū)),另一個(gè)指向它的后一個(gè)節(jié)點(diǎn)(即直接后繼)。這種設(shè)計(jì)使得雙向鏈表可以進(jìn)行雙向遍歷,即可以從頭節(jié)點(diǎn)開(kāi)始向前遍歷,也可以從尾節(jié)點(diǎn)開(kāi)始向后遍歷。
雙向鏈表的節(jié)點(diǎn)結(jié)構(gòu)通常如下所示:
struct Node
{
// 數(shù)據(jù)域
int data;
// 指向直接前驅(qū)的指針
Node* prev;
// 指向直接后繼的指針
Node* next;
};
2.雙向鏈表與單向鏈表的區(qū)別
雙向鏈表的算法描述和單向鏈表基本相同,但是雙向鏈表在刪除和插入節(jié)點(diǎn)時(shí)與單向鏈表有很大的不同:雙向鏈表在刪除節(jié)點(diǎn)時(shí),不但要修改節(jié)點(diǎn)的直接后繼指針,還要同時(shí)修改節(jié)點(diǎn)的直接前驅(qū)指針。在插入時(shí)更是要修改插入節(jié)點(diǎn)的前驅(qū)和后繼的兩個(gè)方向上的指針。
二、雙向鏈表實(shí)現(xiàn)基礎(chǔ)
為了讓實(shí)現(xiàn)的鏈表具有更多的實(shí)用性,鏈表的參數(shù)的數(shù)據(jù)類(lèi)型選擇Objects。Objects參數(shù)可以是1個(gè)也可以2個(gè)甚至更多,總之為了表達(dá)雙向鏈表更能為實(shí)際生活中的需要,這一步就要極盡所能地、契合實(shí)際需要地設(shè)計(jì)Objects的參數(shù)。
1.定義參數(shù)類(lèi)Objects
/// <summary>
/// 定義雙向鏈表的參數(shù)類(lèi)型是Objects類(lèi)型
/// </summary>
/// <param name="value">參數(shù),數(shù)據(jù)數(shù)值</param>
/// <param name="name">參數(shù),數(shù)據(jù)名稱(chēng)</param>
/// <param name="index">參數(shù),數(shù)據(jù)索引</param>
public class Objects(int value, string name, int index)
{
public int Value { get; set; } = value;
public string Name { get; set; } = name;
public int Index { get; set; } = index;
}
2.定義節(jié)點(diǎn)類(lèi)ListNode
// 定義結(jié)點(diǎn)類(lèi)
public class ListNode(Objects obj)
{
public Objects _object = obj;
public ListNode? _next;
public ListNode? previous;
public ListNode? Next
{
get
{
return _next;
}
set
{
_next = value;
}
}
public Objects Object
{
get
{
return _object;
}
set
{
_object = value;
}
}
}3.定義鏈表類(lèi)LinkedList及其方法
在鏈表類(lèi)中要定義兩個(gè)指針_head、_tail,用于正向、逆向查詢(xún);_current用于在鏈表類(lèi)實(shí)時(shí)表達(dá)當(dāng)前節(jié)點(diǎn)指針;
private ListNode? _head; private ListNode? _tail; private ListNode? _current;
鏈表中的方法是自定義的,是滿(mǎn)足實(shí)際需要時(shí)因地制宜設(shè)計(jì)的方法。因此,需要什么方法,就在鏈表中設(shè)計(jì)什么方法。
在本例中,切記一切方法的參數(shù)類(lèi)型都是Objects類(lèi)型的。
(1)在鏈表類(lèi)中定義Append方法、Print方法
public class LinkedList
{
//構(gòu)造函數(shù)
public LinkedList()
{
_ListCountValue = 0;
_head = null;
_tail = null;
}
/// <summary>
/// 鏈表名
/// </summary>
private string listname = "";
public string ListName
{
get
{
return listname;
}
set
{
listname = value;
}
}
/// <summary>
/// 頭指針
/// </summary>
private ListNode? _head;
/// <summary>
/// 尾指針
/// </summary>
private ListNode? _tail;
/// <summary>
/// 定義字段當(dāng)前指針current
/// 定義屬性Current
/// 字段是一種變量,屬性是一種方法
/// </summary>
private ListNode? _current;
public ListNode? Current
{
get
{
return _current;
}
set
{
_current = value;
}
}
// 定義鏈表數(shù)據(jù)的個(gè)數(shù)
private int _ListCountValue;
/// <summary>
/// 尾部添加數(shù)據(jù)
/// </summary>
public void Append(Objects value)
{
ListNode newNode = new(value);
if (IsNull()) //如果頭指針為空
{
_head = newNode;
_tail = newNode;
}
else
{
_tail!.Next = newNode; // 原尾節(jié)點(diǎn)的下節(jié)點(diǎn)=新節(jié)點(diǎn)
newNode.previous = _tail;// 新節(jié)點(diǎn)的前節(jié)點(diǎn)=原尾結(jié)點(diǎn)
_tail = newNode; // 新尾結(jié)點(diǎn)=新節(jié)點(diǎn)
}
_current = newNode; // 當(dāng)前節(jié)點(diǎn)=新節(jié)點(diǎn)
_ListCountValue += 1; // 節(jié)點(diǎn)總數(shù)增加1
}
/// <summary>
/// 刪除當(dāng)前的數(shù)據(jù)
/// </summary>
public void Delete()
{
if (!IsNull()) //若為空鏈表
{
//刪除頭部
if (IsBof())
{
_head = _current!.Next;
_current = _head;
_ListCountValue -= 1;
return;
}
//刪除尾
if (IsEof())
{
_tail = _current!.previous;
_tail!.Next = null;
_current = _tail;
_ListCountValue -= 1;
return;
}
//若刪除中間數(shù)據(jù)
_current!.previous!.Next = _current.Next;
_current = _current.previous;
_ListCountValue -= 1;
return;
}
}(2)在鏈表類(lèi)中定義MoveFirst、MovePrevious、MoveNext、MoveTail、Clear、GetCurrentValue、IsNull、IsEof、IsBof、
/// <summary>
/// 向后移動(dòng)一個(gè)數(shù)據(jù)
/// </summary>
public void MoveNext()
{
if (!IsEof()) _current = _current!.Next;
}
/// <summary>
/// 向前移動(dòng)一個(gè)數(shù)據(jù)
/// </summary>
public void MovePrevious()
{
if (!IsBof()) _current = _current!.previous;
}
/// <summary>
/// 移動(dòng)到第一個(gè)數(shù)據(jù)
/// </summary>
public void MoveFirst()
{
_current = _head;
}
/// <summary>
/// 移動(dòng)到最后一個(gè)數(shù)據(jù)
/// </summary>
public void MoveTail()
{
_current = _tail;
}
/// <summary>
/// 判斷是否為空鏈表
/// </summary>
public bool IsNull()
{
if (_ListCountValue == 0)
return true;
else
return false;
}
/// <summary>
/// 判斷是否為到達(dá)尾部
/// Is End of File
/// </summary>
public bool IsEof()
{
if (_current == _tail)
return true;
else
return false;
}
/// <summary>
/// 判斷是否為到達(dá)頭部
/// Is Beginning of File
/// </summary>
public bool IsBof()
{
if (_current == _head)
return true;
else
return false;
}
/// <summary>
/// 獲取當(dāng)前節(jié)點(diǎn)數(shù)據(jù)
/// </summary>
public Objects GetCurrentValue()
{
return _current!._object;
}
/// <summary>
/// 取得鏈表的數(shù)據(jù)個(gè)數(shù)
/// </summary>
public int ListCount
{
get
{
return _ListCountValue;
}
}
/// <summary>
/// 清空鏈表
/// </summary>
public void Clear()
{
MoveFirst();
while (!IsNull())
{
Delete();
}
}(3)在鏈表類(lèi)中定義Insert()、InsertAscending()、InsertUnAscending()、SortList()
/// <summary>
/// 對(duì)鏈表數(shù)據(jù)冒泡排序
/// </summary>
public static ListNode? SortList(ListNode? head)
{
if (head == null || head.Next == null)
{
return head;
}
bool swapped;
do
{
swapped = false;
ListNode? current = head;
while (current != null && current.Next != null)
{
if (current.Next.Object.Value < current.Object.Value)
{
(current.Next._object, current._object) = (current._object, current.Next._object);
swapped = true;
}
current = current.Next;
}
} while (swapped);
return head;
}
/// <summary>
/// 在當(dāng)前位置前插入數(shù)據(jù)
/// </summary>
public void Insert(Objects value)
{
ListNode newNode = new(value);
if (IsNull())
{
//為空表,則添加
Append(value);
return;
}
if (IsBof())
{
//為頭部插入
newNode.Next = _head;
_head!.previous = newNode;
_head = newNode;
_current = _head;
_ListCountValue += 1;
return;
}
//中間插入
newNode.Next = _current;
newNode.previous = _current!.previous;
_current.previous!.Next = newNode;
_current.previous = newNode;
_current = newNode;
_ListCountValue += 1;
}
/// <summary>
/// 進(jìn)行升序插入
/// </summary>
public void InsertAscending(Objects value)
{
//為空鏈表
if (IsNull())
{
Append(value);
return;
}
//移動(dòng)到頭
MoveFirst();
if (value.Value < GetCurrentValue().Value) //滿(mǎn)足條件,則插入,退出
{
Insert(value);
return;
}
while (true)
{
if (value.Value < GetCurrentValue().Value)//滿(mǎn)足條件,則插入,退出
{
Insert(value);
break;
}
if (IsEof())
{
Append(value);
break;
}
MoveNext();
}
}
/// <summary>
/// 進(jìn)行非升序插入
/// </summary>
public void InsertUnAscending(Objects value)
{
//為空鏈表
if (IsNull())
{
Append(value);
return;
}
//移動(dòng)到頭
MoveFirst();
if (value.Value > GetCurrentValue().Value)//滿(mǎn)足條件,則插入,退出
{
Insert(value);
return;
}
while (true)
{
if (value.Value > GetCurrentValue().Value)//滿(mǎn)足條件,則插入,退出
{
Insert(value);
break;
}
if (IsEof())
{
Append(value);
break;
}
MoveNext();
}
}(4)FindObjects(int value)、FindObjects(string name)、DeleteObjects(string name)、DeleteObjects(int value)
/// <summary>
/// 根據(jù)數(shù)據(jù)名稱(chēng)查詢(xún)數(shù)據(jù)
/// </summary>
/// <param name="name">
/// 數(shù)據(jù)名稱(chēng)
/// </param>
public Objects? FindObjects(string name)
{
ListNode? nodes = _head;
if (IsNull())
{
return null;
}
else if (IsEof())
{
return null;
}
else
while (nodes!._object.Name != name)
{
if (nodes.Next == null)
{
_current = nodes;
return null;
}
nodes = nodes.Next;
}
_current = nodes;
return nodes._object;
}
/// <summary>
/// 根據(jù)參數(shù)值查詢(xún)數(shù)據(jù)
/// </summary>
/// <param name="value">
/// 數(shù)據(jù)編號(hào)
/// </param>
public Objects? FindObjects(int value)
{
ListNode? nodes = _head;
if (IsNull())
{
return null;
}
else if (IsEof())
{
return null;
}
else
while (nodes!._object.Value != value)
{
if (nodes.Next == null)
{
_current = nodes;
return null;
}
nodes = nodes.Next;
}
_current = nodes;
return nodes._object;
}
/// <summary>
/// 根據(jù)數(shù)據(jù)名稱(chēng)刪除數(shù)據(jù)
/// </summary>
/// <param name="name">
/// 數(shù)據(jù)名稱(chēng)
/// </param>
public Objects? DeleteObjects(string name)
{
Objects? objects;
objects = FindObjects(name);
Delete();
return objects;
}
/// <summary>
/// 根據(jù)參數(shù)值刪除數(shù)據(jù)
/// </summary>
/// <param name="value">
/// 數(shù)據(jù)編號(hào)
/// </param>
public Objects? DeleteObjects(int value)
{
Objects? objects;
objects = FindObjects(value);
Delete();
return objects;
}
public void Print()
{
ListNode? current = _head;
while (current != null)
{
Console.WriteLine(current._object.Value);
current = current.Next;
}
}
public void Print()
{
ListNode? current = _head;
while (current != null)
{
Console.WriteLine(current._object.Value);
current = current.Next;
}
}
}三、一些Objects類(lèi)對(duì)象操作技巧
1.Objects類(lèi)對(duì)象用于比較
Objects類(lèi)對(duì)象是復(fù)合類(lèi)型的數(shù)據(jù),本身不能用于比較,但是其int類(lèi)型的屬性值是可以進(jìn)行比較的。比如,應(yīng)該使用“Value”屬性來(lái)訪(fǎng)問(wèn)對(duì)象的整數(shù)值。
ListNode newNode = new(obj);
if (_head == null || _head.Object.Value >= obj.Value)
{
newNode.Next = _head;
return newNode;
}
else
{
ListNode? previous = _head;
ListNode? current = _head!.Next;
while (current != null && current.Object.Value < obj.Value)
{
previous = current;
current = current!.Next;
}
newNode!.Next = current;
previous!.Next = newNode;
}if (_head != null)
{
return _current!.Object.Value;
}
else
{
return default; // 或者 return 0;
}
while (current != null && current.Next != null)
{
if (current.Next.Object.Value < current.Object.Value)
{
(current.Next.Object.Value, current.Object.Value) = (current.Object.Value, current.Next.Object.Value);
swapped = true;
}
current = current.Next;
}
2. Objects類(lèi)對(duì)象用于鏈表的初始化
Objects類(lèi)對(duì)象是復(fù)合類(lèi)型的數(shù)據(jù),用于鏈表初始化時(shí)要復(fù)雜一些,不再向單純的int、string類(lèi)型賦值得那么簡(jiǎn)單直接。
Objects類(lèi)對(duì)象用于鏈表初始化要體現(xiàn) Objects類(lèi)的定義,實(shí)參和形參的數(shù)量和類(lèi)型要一一對(duì)應(yīng)。
LinkedList linkedList = new(); linkedList.Append(new Objects(5, "Five", 1)); linkedList.Append(new Objects(2, "Two", 2)); linkedList.Append(new Objects(8, "Eight", 3)); linkedList.Append(new Objects(1, "One", 4));
四、實(shí)例Main()
在實(shí)例的雙向鏈表類(lèi)中,設(shè)計(jì)一個(gè)Append方法向鏈表的末尾追加初始數(shù)據(jù)5,2,8,1。然后用Print方法顯示鏈表數(shù)據(jù)。
鏈表類(lèi)中的其他方法的示例,詳見(jiàn)Main方法。
class Program
{
static void Main(string[] args)
{
ArgumentNullException.ThrowIfNull(args);
LinkedList list = new();
// 插入結(jié)點(diǎn)
list.Append(new Objects(5, "five", 0));
list.Append(new Objects(2, "two", 1));
list.Append(new Objects(8, "eight", 2));
list.Append(new Objects(1, "one", 3));
list.Append(new Objects(3, "three", 4));
// 獲取當(dāng)前結(jié)點(diǎn)的值
Console.Write("當(dāng)前結(jié)點(diǎn)的值:");
Console.WriteLine(list.GetCurrentValue().Value);
// 移動(dòng)到第一個(gè)結(jié)點(diǎn)
list.MoveFirst();
Console.Write("第一結(jié)點(diǎn)的值:");
Console.WriteLine(list.GetCurrentValue().Value);
// 移動(dòng)到下一個(gè)結(jié)點(diǎn)
list.MoveNext();
Console.Write("下一結(jié)點(diǎn)的值:");
Console.WriteLine(list.GetCurrentValue().Value);
// 移動(dòng)到上一個(gè)結(jié)點(diǎn)
list.MovePrevious();
Console.Write("上一結(jié)點(diǎn)的值:");
Console.WriteLine(list.GetCurrentValue().Value);
list.Print();
Console.WriteLine("*初始數(shù)據(jù)*");
// 刪除尾首2個(gè)結(jié)點(diǎn)
list.MoveTail();
list.Delete();
list.MoveFirst();
list.Delete();
list.Print();
Console.WriteLine("*刪除節(jié)點(diǎn)*");
// 插入升序結(jié)點(diǎn)
LinkedList.SortList(list._head);//先排序
list.InsertAscending(new Objects(6, "six", 5));
list.InsertAscending(new Objects(4, "four", 6));
list.InsertAscending(new Objects(9, "none", 7));
list.Print();
Console.WriteLine("*升序插入*");
// 插入非升序結(jié)點(diǎn)
list.InsertUnAscending(new Objects(7, "seven", 8));
list.InsertUnAscending(new Objects(3, "three", 9));
list.InsertUnAscending(new Objects(10, "ten", 10));
list.Print();
Console.WriteLine("*非升序插入*");
// 清空鏈表
list.Clear();
list.Print();
Console.WriteLine();
Console.WriteLine("**清空就沒(méi)有了**");
// 插入數(shù)據(jù)
list.Insert(new Objects(0, "zero", 11));
list.Insert(new Objects(1, "one", 12));
list.Insert(new Objects(2, "two", 13));
list.Insert(new Objects(3, "three", 14));
list.Print();
Console.WriteLine("*新數(shù)據(jù)*");
Console.WriteLine(list.GetCurrentValue().Value);
Console.WriteLine("*最后插入的是當(dāng)前值*");
list.MoveFirst();
Console.WriteLine(list.GetCurrentValue().Value);
Console.WriteLine("*當(dāng)前值居然是頭節(jié)點(diǎn)*");
list.Insert(new Objects(5, "five", 15));
list.Print();
Console.WriteLine("*在當(dāng)前值前面插入5*");
list.MoveNext();
list.Insert(new Objects(6, "six", 16));
list.Print();
Console.WriteLine("*在當(dāng)前節(jié)點(diǎn)的下節(jié)點(diǎn)前面插入6*");
list.MoveFirst();
list.MovePrevious();
list.Insert(new Objects(7, "seven", 17));
list.Print();
Console.WriteLine("*頭節(jié)點(diǎn)沒(méi)有前節(jié)點(diǎn)故在頭節(jié)點(diǎn)前面插入7*");
list.FindObjects(3);
Console.WriteLine(list.GetCurrentValue().Value);
Console.WriteLine("*按數(shù)據(jù)值3查找*");
list.FindObjects("three");
Console.WriteLine(list.GetCurrentValue().Value);
Console.WriteLine("*按參數(shù)名稱(chēng)three查找*");
list.DeleteObjects(3);
list.Print();
Console.WriteLine("*已經(jīng)刪除了值=3的參數(shù)*");
list.DeleteObjects("six");
list.Print();
Console.WriteLine("*已經(jīng)刪除了名稱(chēng)=six的參數(shù)*");
}
}運(yùn)行結(jié)果:
當(dāng)前結(jié)點(diǎn)的值:3
第一結(jié)點(diǎn)的值:5
下一結(jié)點(diǎn)的值:2
上一結(jié)點(diǎn)的值:5
5
2
8
1
3
*初始數(shù)據(jù)*
2
8
1
*刪除節(jié)點(diǎn)*
1
2
4
6
8
9
*升序插入*
10
7
3
1
2
4
6
8
9
*非升序插入*
**清空就沒(méi)有了**
3
2
1
0
*新數(shù)據(jù)*
3
*最后插入的是當(dāng)前值*
3
*當(dāng)前值居然是頭節(jié)點(diǎn)*
5
3
2
1
0
*在當(dāng)前值前面插入5*
5
6
3
2
1
0
*在當(dāng)前節(jié)點(diǎn)的下節(jié)點(diǎn)前面插入6*
7
5
6
3
2
1
0
*頭節(jié)點(diǎn)沒(méi)有前節(jié)點(diǎn)故在頭節(jié)點(diǎn)前面插入7*
3
*按數(shù)據(jù)值3查找*
3
*按參數(shù)名稱(chēng)three查找*
7
5
6
2
1
0
*已經(jīng)刪除了值=3的參數(shù)*
7
5
2
1
0
*已經(jīng)刪除了名稱(chēng)=six的參數(shù)*

到此這篇關(guān)于一文探索C#中實(shí)現(xiàn)雙向鏈表的方法的文章就介紹到這了,更多相關(guān)C#雙向鏈表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Jquery+Ajax+Json+存儲(chǔ)過(guò)程實(shí)現(xiàn)高效分頁(yè)
這篇文章主要介紹Jquery+Ajax+Json+存儲(chǔ)過(guò)程實(shí)現(xiàn)分頁(yè),需要的朋友可以參考下2015-08-08
C# windows語(yǔ)音識(shí)別與朗讀實(shí)例
這篇文章主要為大家詳細(xì)介紹了C# windows語(yǔ)音識(shí)別與朗讀實(shí)例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
WPF自定義控件實(shí)現(xiàn)ItemsControl魚(yú)眼效果
這篇文章主要為大家詳細(xì)介紹了WPF如何通過(guò)自定義控件實(shí)現(xiàn)ItemsControl魚(yú)眼效果,文中的示例代碼講解詳細(xì),需要的可以參考一下2024-01-01

