C#數(shù)據(jù)結(jié)構(gòu)之雙向鏈表(DbLinkList)實(shí)例詳解
本文實(shí)例講述了C#數(shù)據(jù)結(jié)構(gòu)之雙向鏈表(DbLinkList)。分享給大家供大家參考,具體如下:
這是繼上一篇《C#數(shù)據(jù)結(jié)構(gòu)之單鏈表(LinkList)實(shí)例詳解》的繼續(xù),對(duì)于雙向鏈接,節(jié)點(diǎn)上除了Next屬性外,還要有Prev屬性用來(lái)指向前一個(gè)節(jié)點(diǎn),DbNode定義如下:
namespace 線性表
{
public class DbNode<T>
{
private T data;
private DbNode<T> prev;
private DbNode<T> next;
public DbNode(T data, DbNode<T> next,DbNode<T> prev)
{
this.data = data;
this.next = next;
this.prev = prev;
}
public DbNode(T data, DbNode<T> next)
{
this.data = data;
this.next = next;
this.prev = null;
}
public DbNode(DbNode<T> next)
{
this.data = default(T);
this.next = next;
this.prev = null;
}
public DbNode(T data)
{
this.data = data;
this.next = null;
this.prev = null;
}
public DbNode()
{
data = default(T);
next = null;
prev = null;
}
public T Data
{
set { this.data = value; }
get { return this.data; }
}
public DbNode<T> Prev
{
get { return prev; }
set { prev = value; }
}
public DbNode<T> Next
{
get { return next; }
set { next = value; }
}
}
}
雙鏈表的插入操作要稍微復(fù)雜一點(diǎn),示意圖如下:
同樣對(duì)于刪除操作,也要額外處理prev指向
完整實(shí)現(xiàn)DbLinkList<T>:
using System;
using System.Text;
namespace 線性表
{
public class DbLinkList<T> : IListDS<T>
{
private DbNode<T> head;
public DbNode<T> Head
{
get { return head; }
set { head = value; }
}
public DbLinkList()
{
head = null;
}
/// <summary>
/// 類(lèi)索引器
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T this[int index]
{
get
{
return this.GetItemAt(index);
}
}
/// <summary>
/// 返回單鏈表的長(zhǎng)度
/// </summary>
/// <returns></returns>
public int Count()
{
DbNode<T> p = head;
int len = 0;
while (p != null)
{
len++;
p = p.Next;
}
return len;
}
/// <summary>
/// 清空
/// </summary>
public void Clear()
{
head = null;
}
/// <summary>
/// 是否為空
/// </summary>
/// <returns></returns>
public bool IsEmpty()
{
return head == null;
}
/// <summary>
/// 在最后附加元素
/// </summary>
/// <param name="item"></param>
public void Append(T item)
{
DbNode<T> d = new DbNode<T>(item);
DbNode<T> n = new DbNode<T>();
if (head == null)
{
head = d;
return;
}
n = head;
while (n.Next != null)
{
n = n.Next;
}
n.Next = d;
d.Prev = n;
}
//前插
public void InsertBefore(T item, int i)
{
if (IsEmpty() || i < 0)
{
Console.WriteLine("List is empty or Position is error!");
return;
}
//在最開(kāi)頭插入
if (i == 0)
{
DbNode<T> q = new DbNode<T>(item);
q.Next = head;//把"頭"改成第二個(gè)元素
head.Prev = q;
head = q;//把自己設(shè)置為"頭"
return;
}
DbNode<T> n = head;
DbNode<T> d = new DbNode<T>();
int j = 0;
//找到位置i的前一個(gè)元素d
while (n.Next != null && j < i)
{
d = n;
n = n.Next;
j++;
}
if (n.Next == null) //說(shuō)明是在最后節(jié)點(diǎn)插入(即追加)
{
DbNode<T> q = new DbNode<T>(item);
n.Next = q;
q.Prev = n;
q.Next = null;
}
else
{
if (j == i)
{
DbNode<T> q = new DbNode<T>(item);
d.Next = q;
q.Prev = d;
q.Next = n;
n.Prev = q;
}
}
}
/// <summary>
/// 在位置i后插入元素item
/// </summary>
/// <param name="item"></param>
/// <param name="i"></param>
public void InsertAfter(T item, int i)
{
if (IsEmpty() || i < 0)
{
Console.WriteLine("List is empty or Position is error!");
return;
}
if (i == 0)
{
DbNode<T> q = new DbNode<T>(item);
q.Next = head.Next;
head.Next.Prev = q;
head.Next = q;
q.Prev = head;
return;
}
DbNode<T> p = head;
int j = 0;
while (p != null && j < i)
{
p = p.Next;
j++;
}
if (j == i)
{
DbNode<T> q = new DbNode<T>(item);
q.Next = p.Next;
if (p.Next != null)
{
p.Next.Prev = q;
}
p.Next = q;
q.Prev = p;
}
else
{
Console.WriteLine("Position is error!");
}
}
/// <summary>
/// 刪除位置i的元素
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public T RemoveAt(int i)
{
if (IsEmpty() || i < 0)
{
Console.WriteLine("Link is empty or Position is error!");
return default(T);
}
DbNode<T> q = new DbNode<T>();
if (i == 0)
{
q = head;
head = head.Next;
head.Prev = null;
return q.Data;
}
DbNode<T> p = head;
int j = 0;
while (p.Next != null && j < i)
{
j++;
q = p;
p = p.Next;
}
if (j == i)
{
p.Next.Prev = q;
q.Next = p.Next;
return p.Data;
}
else
{
Console.WriteLine("The node is not exist!");
return default(T);
}
}
/// <summary>
/// 獲取指定位置的元素
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public T GetItemAt(int i)
{
if (IsEmpty())
{
Console.WriteLine("List is empty!");
return default(T);
}
DbNode<T> p = new DbNode<T>();
p = head;
if (i == 0)
{
return p.Data;
}
int j = 0;
while (p.Next != null && j < i)
{
j++;
p = p.Next;
}
if (j == i)
{
return p.Data;
}
else
{
Console.WriteLine("The node is not exist!");
return default(T);
}
}
//按元素值查找索引
public int IndexOf(T value)
{
if (IsEmpty())
{
Console.WriteLine("List is Empty!");
return -1;
}
DbNode<T> p = new DbNode<T>();
p = head;
int i = 0;
while (!p.Data.Equals(value) && p.Next != null)
{
p = p.Next;
i++;
}
return i;
}
/// <summary>
/// 元素反轉(zhuǎn)
/// </summary>
public void Reverse()
{
DbLinkList<T> result = new DbLinkList<T>();
DbNode<T> t = this.head;
result.Head = new DbNode<T>(t.Data);
t = t.Next;
//(把當(dāng)前鏈接的元素從head開(kāi)始遍歷,逐個(gè)插入到另一個(gè)空鏈表中,這樣得到的新鏈表正好元素順序跟原鏈表是相反的)
while (t!=null)
{
result.InsertBefore(t.Data, 0);
t = t.Next;
}
this.head = result.head;//將原鏈表直接掛到"反轉(zhuǎn)后的鏈表"上
result = null;//顯式清空原鏈表的引用,以便讓GC能直接回收
}
//得到某個(gè)指定的節(jié)點(diǎn)(為了下面測(cè)試從后向前遍歷)
private DbNode<T> GetNodeAt(int i){
if (IsEmpty())
{
Console.WriteLine("List is empty!");
return null;
}
DbNode<T> p = new DbNode<T>();
p = head;
if (i == 0)
{
return p;
}
int j = 0;
while (p.Next != null && j < i)
{
j++;
p = p.Next;
}
if (j == i)
{
return p;
}
else
{
Console.WriteLine("The node is not exist!");
return null;
}
}
/// <summary>
/// 測(cè)試用prev屬性從后面開(kāi)始遍歷
/// </summary>
/// <returns></returns>
public string TestPrevErgodic()
{
DbNode<T> tail = GetNodeAt(Count() - 1);
StringBuilder sb = new StringBuilder();
sb.Append(tail.Data.ToString() + ",");
while (tail.Prev != null)
{
sb.Append(tail.Prev.Data.ToString() + ",");
tail = tail.Prev;
}
return sb.ToString().TrimEnd(',');
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
DbNode<T> n = this.head;
sb.Append(n.Data.ToString() + ",");
while (n.Next != null)
{
sb.Append(n.Next.Data.ToString() + ",");
n = n.Next;
}
return sb.ToString().TrimEnd(',');
}
}
}
測(cè)試代碼片段:
Console.WriteLine("-------------------------------------");
Console.WriteLine("雙鏈表測(cè)試開(kāi)始...");
DbLinkList<string> dblink = new DbLinkList<string>();
dblink.Head = new DbNode<string>("x");
dblink.InsertBefore("w", 0);
dblink.InsertBefore("v", 0);
dblink.Append("y");
dblink.InsertBefore("z", dblink.Count());
Console.WriteLine(dblink.Count());//5
Console.WriteLine(dblink.ToString());//v,w,x,y,z
Console.WriteLine(dblink[1]);//w
Console.WriteLine(dblink[0]);//v
Console.WriteLine(dblink[4]);//z
Console.WriteLine(dblink.IndexOf("z"));//4
Console.WriteLine(dblink.RemoveAt(2));//x
Console.WriteLine(dblink.ToString());//v,w,y,z
dblink.InsertBefore("x", 2);
Console.WriteLine(dblink.ToString());//v,w,x,y,z
Console.WriteLine(dblink.GetItemAt(2));//x
dblink.Reverse();
Console.WriteLine(dblink.ToString());//z,y,x,w,v
dblink.InsertAfter("1", 0);
dblink.InsertAfter("2", 1);
dblink.InsertAfter("6", 5);
dblink.InsertAfter("8", 7);
dblink.InsertAfter("A", 10);//Position is error!
Console.WriteLine(dblink.ToString()); //z,1,2,y,x,w,6,v,8
string _tail = dblink.GetItemAt(dblink.Count()-1);
Console.WriteLine(_tail);
Console.WriteLine(dblink.TestPrevErgodic());//8
Console.ReadKey(); //8,v,6,w,x,y,2,1,z
當(dāng)然從上面的測(cè)試代碼中,似乎并不能看出雙鏈表的優(yōu)點(diǎn),雙鏈表的好處在于,如果需要在鏈表中,需要通過(guò)某個(gè)節(jié)點(diǎn)得到它的前驅(qū)節(jié)點(diǎn)時(shí),雙鏈表直接用prev屬性就能找到;而單鏈表要做到這一點(diǎn),必須再次從Head節(jié)點(diǎn)開(kāi)始一個(gè)一個(gè)用Next向下找,這樣時(shí)間復(fù)雜度從O(n)降到O(1),顯然更有效率。
注:如果把雙鏈表再做一下改造,讓頭尾接起來(lái),即Head的Prev屬性指向最后一個(gè)節(jié)點(diǎn)(就叫做Tail吧),同時(shí)把Tail節(jié)點(diǎn)的Next屬性指向Head節(jié)點(diǎn),就形成了所謂的“循環(huán)雙向鏈表”
當(dāng)然,這樣的結(jié)構(gòu)可以在鏈表中再增加一個(gè)Tail節(jié)點(diǎn)屬性,在做元素插入或刪除時(shí),可以循環(huán)到底以更新尾節(jié)點(diǎn)Tail(當(dāng)然這樣會(huì)給插入/刪除元素帶來(lái)一些額外的開(kāi)銷(xiāo)),但是卻可以給GetItemAt(int i)方法帶來(lái)優(yōu)化的空間,比如要查找的元素在前半段時(shí),可以從Head開(kāi)始用next向后找;反之,如果要找的元素在后半段,則可以從Tail節(jié)點(diǎn)用prev屬性向前找。
注:.Net中微軟已經(jīng)給出了一個(gè)內(nèi)置的雙向鏈表System.Collections.Generic.LinkedList<T>,在了解雙鏈表的原理后,建議大家直接系統(tǒng)內(nèi)置的鏈表。
希望本文所述對(duì)大家C#程序設(shè)計(jì)有所幫助。
上一篇:C#編程實(shí)現(xiàn)連接SQL SERVER數(shù)據(jù)庫(kù)實(shí)例詳解
欄 目:C#教程
下一篇:C#編程實(shí)現(xiàn)查看剪切板內(nèi)容的方法
本文標(biāo)題:C#數(shù)據(jù)結(jié)構(gòu)之雙向鏈表(DbLinkList)實(shí)例詳解
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/6802.html
您可能感興趣的文章
- 01-10C#一個(gè)簡(jiǎn)單的定時(shí)小程序?qū)崿F(xiàn)代碼
- 01-10微信開(kāi)放平臺(tái)之網(wǎng)站授權(quán)微信登錄功能
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類(lèi)型和變量二
- 01-10C#編程自學(xué)之開(kāi)篇介紹
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類(lèi)型和變量三
- 01-10C#編程自學(xué)之運(yùn)算符和表達(dá)式
- 01-10C#編程自學(xué)之類(lèi)和對(duì)象
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類(lèi)型和變量一
- 01-10C#編程自學(xué)之流程控制語(yǔ)句
- 01-10C#基于委托實(shí)現(xiàn)多線程之間操作的方法


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開(kāi)的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過(guò)重寫(xiě)Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改


