雙向鏈表(雙鏈表)是鏈表的一種。和單鏈表同樣,雙鏈表也是由節點組成,它的每一個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。html
實現:接口ide
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _001_線性表 { interface IListDS<T> { int GetLength(); void Clear(); bool IsEmpty(); void Add(T item); void Insert(T item, int index); T Delete(int index); T this[int index] { get; } T GetEle(int index); int Locate(T value); } }
雙向節點:oop
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace _001_線性表 { /// <summary> /// 雙向節點 /// </summary> /// <typeparam name="T"></typeparam> class DbNode<T> { private T data; private DbNode<T> prev; private DbNode<T> next; public DbNode(T val,DbNode<T> p,DbNode<T> n) { this.data = val; this.prev = p; this.next = n; } public DbNode(DbNode<T> p) { next = p; } public DbNode(T val) { data = val; next = null; prev = null; } public DbNode() { data = default(T); next = null; prev = null; } public T Data { get { return data; } set { data = value; } } public DbNode<T> Prev { get { return prev; } set { prev = value; } } public DbNode<T> Next { get { return next; } set { next = value; } } public void SetNode(DbNode<T> pre,DbNode<T> next) { this.prev = pre; this.next = next; } } }
雙向鏈表:this
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace _001_線性表 { /// <summary> /// 雙向鏈表 /// </summary> /// <typeparam name="T"></typeparam> class DoubleLink<T>:IListDS<T> { #region IListDS<T> 成員 private DbNode<T> _linkHead; public DoubleLink() { _linkHead = null; } public int GetLength() { if (IsEmpty()) return 0; DbNode<T> temp = _linkHead; int length = 1; while (temp.Next != null) { length++; temp = temp.Next; } return length; } public void Clear() { _linkHead = null; } public bool IsEmpty() { return _linkHead == null; } /// <summary> /// 在雙向鏈表的尾端添加一個新數據 /// </summary> /// <param name="item"></param> public void Add(T item) { DbNode<T> newNode = new DbNode<T>(item); if (IsEmpty()) { _linkHead = newNode; } else { DbNode<T> preNode = GetListItem(this.GetLength() - 1); newNode.Prev = newNode; preNode.Next = newNode.Prev; newNode.Prev = preNode; } } /// <summary> /// 獲取指定位置的雙向鏈表項目,頭項從0開始 /// </summary> public DbNode<T> GetListItem(int index) { if (index < 0) throw new Exception("索引不能小於0"); if (index > this.GetLength() - 1) throw new Exception("索引超出列表總長度"); DbNode<T> temp = _linkHead; for (int i = 0; i < index; i++) { temp = temp.Next; } return temp; } /// <summary> /// 在雙向鏈表指定的位置插入一個新項 /// </summary> public void Insert(T item, int index) { if (index < 0) throw new Exception("插入位置不能小於0"); if (index > this.GetLength() + 1) throw new Exception("插入位置超出鏈表長度"); DbNode<T> newNode = new DbNode<T>(item); if (index == 0) { newNode.Next = _linkHead; _linkHead = newNode; } else { DbNode<T> preNode = GetListItem(index - 1); //要插入位置前面的節點 DbNode<T> atfNode = GetListItem(index);//要插入位置後面的節點 preNode.Next = new DbNode<T>(item, preNode, atfNode); } } /// <summary> /// 刪除指定位置的雙向鏈表項 /// </summary> /// <param name="index"></param> public T Delete(int index) { if (index < 0) throw new Exception("刪除位置不能小於0"); if (index > this.GetLength() - 1) throw new Exception("插入位置超出鏈表長度"); T data = default(T); if (index == 0) { data = _linkHead.Data; _linkHead = _linkHead.Next; if(!IsEmpty()) this._linkHead.Prev = null; } else { DbNode<T> DelNode = GetListItem(index); //取到要刪除的節點 data = DelNode.Data; DbNode<T> preNode = DelNode.Prev; DbNode<T> nextNode = DelNode.Next; preNode.Next = nextNode; nextNode.Prev = preNode; } return data; } public T this[int index] { get { return GetEle(index); } } public T GetEle(int index) { DbNode<T> temp = _linkHead; for (int i = 0; i < index; i++) { temp = temp.Next; } return temp.Data; } public int Locate(T value) { DbNode<T> temp = _linkHead; if (IsEmpty()) { return -1; } else { int index = 0; while (true) { if (!temp.Data.Equals(value)) { if (temp.Next != null) { index++; temp = temp.Next; } else { break; } } else { return index; } } return -1; } } #endregion } }
從雙向鏈表中的任意一個結點開始,均可以很方便地訪問它的前驅結點和後繼結點。通常咱們都構造雙向循環鏈表。spa
雙鏈表的示意圖以下:指針
表頭爲空,表頭的後繼節點爲"節點10"(數據爲10的節點);"節點10"的後繼節點是"節點20"(數據爲10的節點),"節點20"的前繼節點是"節點10";"節點20"的後繼節點是"節點30","節點30"的前繼節點是"節點20";...;末尾節點的後繼節點是表頭。code
刪除"節點30"
刪除以前:"節點20"的後繼節點爲"節點30","節點30" 的前繼節點爲"節點20"。"節點30"的後繼節點爲"節點40","節點40" 的前繼節點爲"節點30"。
刪除以後:"節點20"的後繼節點爲"節點40","節點40" 的前繼節點爲"節點20"。htm
在"節點10"與"節點20"之間添加"節點15"
添加以前:"節點10"的後繼節點爲"節點20","節點20" 的前繼節點爲"節點10"。
添加以後:"節點10"的後繼節點爲"節點15","節點15" 的前繼節點爲"節點10"。"節點15"的後繼節點爲"節點20","節點20" 的前繼節點爲"節點15"。blog
實現:索引
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _001_線性表 { /// <summary> /// 雙向循環鏈表 /// </summary> /// <typeparam name="T"></typeparam> class DoubleLoopLink<T> : IListDS<T> { private DbNode<T> _linkHead; public T this[int index] =>GetEle(index); public void Add(T item) { DbNode<T> newNode = new DbNode<T>(item, null, null); if (IsEmpty()) { _linkHead = newNode; _linkHead.Prev = _linkHead; _linkHead.Next = _linkHead; } else { DbNode<T> preNode = _linkHead; while (preNode.Next != _linkHead) { preNode = preNode.Next; } preNode.Next = newNode;//鏈表的尾部 的後指針 指向新的節點 newNode.SetNode(preNode, _linkHead); _linkHead.Prev = newNode; //頭部的 前指針 指向 新的尾部 } } public void Clear() { _linkHead = null; } public T Delete(int index) { T data = default(T); if (IsEmpty()) throw new Exception("鏈表爲空,沒有可清除的項"); if (index < 0 || index > this.GetLength() - 1) throw new Exception("給定索引超出鏈表長度"); DbNode<T> preNode = _linkHead; if (index == 0) { while (preNode.Next != _linkHead) { preNode = preNode.Next; } this._linkHead = _linkHead.Next; this._linkHead.Prev = preNode; data = preNode.Next.Data; preNode.Next = this._linkHead; } else { for (int i = 1; i < index - 1; i++) { preNode = preNode.Next; } //看圖比較好理解 DbNode<T> aftNode = preNode.Next.Next; //要刪除的節點 後面的節點 preNode.Next = aftNode;//要刪除節點的前面節點 的後指針 指向 要刪除的節點 後面的節點 aftNode.Prev = preNode; //要刪除的節點 前面的節點 指向 要刪除節點的前面節點 } return data; } public T GetEle(int index) { DbNode<T> temp = _linkHead; for (int i = 0; i < index; i++) { temp = temp.Next; } return temp.Data; } public int GetLength() { if (IsEmpty()) return 0; DbNode<T> temp = _linkHead; int length = 1; while (temp.Next != _linkHead) { length++; temp = temp.Next; } return length; } /// <summary> /// 插入 /// </summary> /// <param name="item"></param> /// <param name="index"></param> public void Insert(T item, int index) { if (IsEmpty()) throw new Exception("數據鏈表爲空"); if (index < 0 || index > this.GetLength()) throw new Exception("給定索引超出鏈表長度"); DbNode<T> newNode = new DbNode<T>(item); DbNode<T> preNode = _linkHead; if (index == 0) //等於零 先找到 鏈表中 head的前一個節點 這個節點鏈接 head { while (preNode.Next != _linkHead) { preNode = preNode.Next; } preNode.Next = newNode; newNode.SetNode(preNode, _linkHead); _linkHead.Prev = newNode; return; } else { for (int i = 1; i < index - 1; i++) { preNode = preNode.Next; } //preNode要插入位置的前節點 DbNode<T> atfNode = preNode.Next;//要插入節點的後節點 preNode.Next = newNode; //後指針 連上新節點 newNode.SetNode(preNode,atfNode); atfNode.Prev = newNode;//要插入節點的後節點 連上 新節點 } } public bool IsEmpty() { return _linkHead == null; } public int Locate(T value) { if (IsEmpty()) throw new Exception("鏈表爲空"); DbNode<T> preNode = _linkHead; int index = 0; while (true) { if (!preNode.Data.Equals(value)) { if (preNode.Next != _linkHead) { index++; preNode = preNode.Next; } else { break; } } else { return index; } } return -1; } } }
基本的鏈表大概就是這樣了。