重溫?cái)?shù)據(jù)結(jié)構(gòu)系列隨筆:?jiǎn)捂湵恚╟#模擬實(shí)現(xiàn))
重溫?cái)?shù)據(jù)結(jié)構(gòu)系列隨筆:?jiǎn)捂湵恚╟#模擬實(shí)現(xiàn))
上一節(jié)我們講述了數(shù)據(jù)結(jié)構(gòu)的基本概念,這一節(jié)讓我們來(lái)討論下單鏈表的概念和實(shí)現(xiàn)
我從書(shū)中簡(jiǎn)單摘錄下單鏈表概念

簡(jiǎn)單而言單鏈表的是通過(guò)許多節(jié)點(diǎn)構(gòu)成,每個(gè)節(jié)點(diǎn)包含2個(gè)重要元素:該節(jié)點(diǎn)數(shù)據(jù)(數(shù)據(jù)域)和指向下個(gè)節(jié)點(diǎn)的地址(指針域)
這樣說(shuō)太枯燥了,讓我們直接用c# 來(lái)一步步實(shí)現(xiàn)
既然一個(gè)節(jié)點(diǎn)是由(數(shù)據(jù)域)和(指針域)構(gòu)成,那我們簡(jiǎn)單DIY一個(gè)LinkNode類
/// <summary>
/// 單鏈表的節(jié)點(diǎn)
/// </summary>
public class LinkNode
{
//節(jié)點(diǎn)數(shù)據(jù)域
public object LinkNodeData { get; set; }
//自己節(jié)點(diǎn)的地址
public Guid SelfAddress { get; set; }
//下個(gè)節(jié)點(diǎn)的地址指針(存儲(chǔ)位置)
public Guid NextAddress { get; set; }
}
繼續(xù)來(lái)了解概念了,既然節(jié)點(diǎn)準(zhǔn)備好了,那我們要了解節(jié)點(diǎn)是怎么通過(guò)指針域連接在一起的,看圖

圖中節(jié)點(diǎn)就是一個(gè)小矩形,數(shù)據(jù)域是姓名,指針域就是那個(gè)箭頭所表示的指向它的后繼,頭節(jié)點(diǎn)h->zhao->Qian->....Wang
這樣連接起來(lái)就是一個(gè)完整的單鏈表,頭結(jié)點(diǎn)的數(shù)據(jù)域可以是任何信息,尾節(jié)點(diǎn)的地址域是空(他沒(méi)有后繼節(jié)點(diǎn)了)
好,代碼中我們只有node 沒(méi)有LinkTable,那我們就按照上圖來(lái)建立一個(gè)LinkTable類
public class LinkTable
{
//定義一個(gè)LinkNode集合
List<LinkNode> list = new List<LinkNode>();
public LinkTable()
{
}
/// <summary>
/// 進(jìn)行單向鏈表初始化
/// </summary>
public void InitialList()
{
//添加5個(gè)節(jié)點(diǎn)擁有唯一的guid作為自身地址,后繼節(jié)點(diǎn)地址為guid.empty
for (int i = 0; i < 5; i++)
{
list.Add
(
new LinkNode { LinkNodeData = string.Format("第{0}個(gè)節(jié)點(diǎn)", i + 1), SelfAddress = Guid.NewGuid(), NextAddress = Guid.Empty }
);
}
s
var j = 0;
//將節(jié)點(diǎn)的 指針域指向下一個(gè)節(jié)點(diǎn)的地址
list.ForEach
(
(linkNode) =>
{
if (j < list.Count - 1)
{
linkNode.NextAddress = list.Skip(++j).FirstOrDefault().SelfAddress;
}
}
);
}
}
LinkTable類包含一個(gè)LinkNode集合 和一個(gè)初始方法,這個(gè)方法是先添加節(jié)點(diǎn)數(shù)據(jù)到集合中,然后將節(jié)點(diǎn)的地址域一一連接起來(lái)
肯定會(huì)有朋友問(wèn)我,那么你怎么在單鏈表中插入數(shù)據(jù)或刪除數(shù)據(jù)呢?
非常棒的問(wèn)題,看圖:

圖中可以看出a節(jié)點(diǎn)的后繼是b節(jié)點(diǎn),a節(jié)點(diǎn)的指針域指向b節(jié)點(diǎn),那如果在a節(jié)點(diǎn)和b節(jié)點(diǎn)中添加一個(gè)新的節(jié)點(diǎn)那情況又如何?
其實(shí)圖中已經(jīng)表達(dá)出來(lái)了,將a的指針域指向新節(jié)點(diǎn),然后將新節(jié)點(diǎn)的指針域指向b節(jié)點(diǎn)
馬上看代碼理解
既然是添加節(jié)點(diǎn)那我們?cè)贚inkTable類中添加方法就行
/// <summary>
/// 添加一個(gè)新節(jié)點(diǎn)
/// </summary>
/// <param name="node">節(jié)點(diǎn)</param>
/// <param name="addIndex">在index處添加節(jié)點(diǎn)</param>
public void AddNode(LinkNode node, int addIndex)
{
if (this.list == null || node == null)
{
// 如果鏈表為空則初始鏈表并且添加節(jié)點(diǎn)
this.InitialList();
}
if (addIndex < 0 || addIndex > list.Count)
{
throw new IndexOutOfRangeException("removeIndex超出范圍");
}
var listCount = list.Count;
//注意,得到新插入節(jié)點(diǎn)的前一個(gè)索引位置
var prev = addIndex - 1 <= 0 || listCount <= 0 ? 0 : addIndex - 1;
//注意,得到新插入節(jié)點(diǎn)的后一個(gè)索引位置
var after = listCount <= 0 ? 0 :
addIndex > listCount - 1 ? listCount - 1 : addIndex;
//插入后前一個(gè)節(jié)點(diǎn)
var prevNode = list[prev];
//插入后后一個(gè)節(jié)點(diǎn)
var afterNode = list[after];
//將前一個(gè)節(jié)點(diǎn)的指針域指向新節(jié)點(diǎn)
node.NextAddress = afterNode.SelfAddress;
//將新節(jié)點(diǎn)的指針域指向后一個(gè)節(jié)點(diǎn)
prevNode.NextAddress = node.SelfAddress;
//判斷是否插入到最后一個(gè)位置
if (addIndex == list.Count)
{
node.NextAddress = Guid.Empty;
list.Add(node);
}
else
{
// 插入新節(jié)點(diǎn)
list.Insert(addIndex, node);
}
}
代碼注釋能夠幫助你了解下添加節(jié)點(diǎn)的具體過(guò)程,請(qǐng)大家仔細(xì)消化下
最后是刪除一個(gè)節(jié)點(diǎn)的情況:

和添加節(jié)點(diǎn)正好逆向思維,當(dāng)我們刪除b節(jié)點(diǎn)時(shí),我們要將a節(jié)點(diǎn)的指針域指向c節(jié)點(diǎn)保證我們的單鏈表不被破壞
刪除方法同樣寫(xiě)在LinkTable類中
/// <summary>
/// 通過(guò)索引刪除
/// </summary>
/// <param name="removeIndex"></param>
/// <returns></returns>
public void Remove(int removeIndex)
{
if (this.list == null)
{
// 如果鏈表為空則初始鏈表并且添加節(jié)點(diǎn)
this.InitialList();
}
if (removeIndex > this.list.Count - 1 || removeIndex < 0)
{
throw new IndexOutOfRangeException("removeIndex超出范圍");
}
var preIndex = removeIndex - 1;
var afterIndex = removeIndex + 1;
var preNode = list[preIndex];
var afterNode = list[afterIndex];
//將被刪除節(jié)點(diǎn)前后的指針域進(jìn)行整理
preNode.NextAddress = afterNode.SelfAddress;
//刪除該節(jié)點(diǎn)
list.Remove(list[removeIndex]);
}
ok,這就是單鏈表的一個(gè)簡(jiǎn)單理解,請(qǐng)大家務(wù)必牢記,因?yàn)楹笳碌难h(huán)列表將更復(fù)雜,單鏈表只是一個(gè)鏈表的基礎(chǔ)(一以下是完整代碼及輸出情況)
View Code
class Program
{
static void Main(string[] args)
{
LinkTable table = new LinkTable();
//初始化
table.InitialList();
//嘗試添加一個(gè)新節(jié)點(diǎn)
table.AddNode
(
new LinkNode { LinkNodeData="新節(jié)點(diǎn)",NextAddress=Guid.Empty, SelfAddress=Guid.NewGuid()},
4
);
//刪除一個(gè)節(jié)點(diǎn)
table.Remove(1);
var i = 0;
//循環(huán)顯示
table.list.ForEach
(
(linkNode) =>
{
if (i ==table.list.Count - 1)
{
Console.Write("{0} ->{1}", linkNode.LinkNodeData, "Null");
}
else
{
Console.Write("{0} ->", linkNode.LinkNodeData);
}
i++;
}
);
Console.ReadLine();
}
}
/// <summary>
/// 單鏈表的節(jié)點(diǎn)
/// </summary>
public class LinkNode
{
//節(jié)點(diǎn)數(shù)據(jù)域
public object LinkNodeData { get; set; }
//自己節(jié)點(diǎn)的地址
public Guid SelfAddress { get; set; }
//下個(gè)節(jié)點(diǎn)的地址指針(存儲(chǔ)位置)
public Guid NextAddress { get; set; }
}
/// <summary>
/// 單鏈表
/// </summary>
public class LinkTable
{
//定義一個(gè)LinkNode集合
public List<LinkNode> list = new List<LinkNode>();
public LinkTable()
{
}
/// <summary>
/// 進(jìn)行單向鏈表初始化
/// </summary>
public void InitialList()
{
//添加10個(gè)節(jié)點(diǎn)擁有唯一的guid作為自身地址,后繼節(jié)點(diǎn)地址為guid.empty
for (int i = 0; i < 5; i++)
{
list.Add
(
new LinkNode { LinkNodeData = string.Format("第{0}個(gè)節(jié)點(diǎn)", i + 1), SelfAddress = Guid.NewGuid(), NextAddress = Guid.Empty }
);
}
var j = 0;
//將節(jié)點(diǎn)的 指針域指向下一個(gè)節(jié)點(diǎn)的地址
list.ForEach
(
(linkNode) =>
{
if (j < list.Count - 1)
{
linkNode.NextAddress = list.Skip(++j).FirstOrDefault().SelfAddress;
}
}
);
}
/// <summary>
/// 添加一個(gè)新節(jié)點(diǎn)
/// </summary>
/// <param name="node">節(jié)點(diǎn)</param>
/// <param name="addIndex">在index處添加節(jié)點(diǎn)</param>
public void AddNode(LinkNode node, int addIndex)
{
if (this.list == null || node == null)
{
// 如果鏈表為空則初始鏈表并且添加節(jié)點(diǎn)
this.InitialList();
}
if (addIndex < 0 || addIndex > list.Count)
{
throw new IndexOutOfRangeException("removeIndex超出范圍");
}
var listCount = list.Count;
//注意,得到新插入節(jié)點(diǎn)的前一個(gè)索引位置
var prev = addIndex - 1 <= 0 || listCount <= 0 ? 0 : addIndex - 1;
//注意,得到新插入節(jié)點(diǎn)的后一個(gè)索引位置
var after = listCount <= 0 ? 0 :
addIndex > listCount - 1 ? listCount - 1 : addIndex;
//插入后前一個(gè)節(jié)點(diǎn)
var prevNode = list[prev];
//插入后后一個(gè)節(jié)點(diǎn)
var afterNode = list[after];
//將前一個(gè)節(jié)點(diǎn)的指針域指向新節(jié)點(diǎn)
node.NextAddress = afterNode.SelfAddress;
//將新節(jié)點(diǎn)的指針域指向后一個(gè)節(jié)點(diǎn)
prevNode.NextAddress = node.SelfAddress;
//判斷是否插入到最后一個(gè)位置
if (addIndex == list.Count)
{
node.NextAddress = Guid.Empty;
list.Add(node);
}
else
{
// 插入新節(jié)點(diǎn)
list.Insert(addIndex, node);
}
}
/// <summary>
/// 通過(guò)索引刪除
/// </summary>
/// <param name="removeIndex"></param>
/// <returns></returns>
public void Remove(int removeIndex)
{
if (this.list == null)
{
// 如果鏈表為空則初始鏈表并且添加節(jié)點(diǎn)
this.InitialList();
}
if (removeIndex > this.list.Count - 1 || removeIndex < 0)
{
throw new IndexOutOfRangeException("removeIndex超出范圍");
}
var preIndex = removeIndex - 1;
var afterIndex = removeIndex + 1;
var preNode = list[preIndex];
var afterNode = list[afterIndex];
//將被刪除節(jié)點(diǎn)前后的指針域進(jìn)行整理
preNode.NextAddress = afterNode.SelfAddress;
//刪除該節(jié)點(diǎn)
list.Remove(list[removeIndex]);
}
}
輸出:

希望大家對(duì)單鏈表有比較深的理解,其實(shí)在效率性能上這樣的單鏈表不及數(shù)組,因?yàn)閿?shù)組更本沒(méi)有那么繁瑣,
大家在實(shí)際項(xiàng)目還是用數(shù)組比較好,下章會(huì)和大家先補(bǔ)充下c#中的LinkList類和Array類的區(qū)別(*數(shù)組和鏈表的區(qū)別(很重要)),
然后簡(jiǎn)單說(shuō)下循環(huán)鏈表。
敬請(qǐng)期待 !

浙公網(wǎng)安備 33010602011771號(hào)