數據結構之雙向鏈表

        雙向鏈表宛如一列火車,剛發明的時候只有一個頭,若是它的行駛路線爲:A->B->C->D->E->F->G->H->I->J->K->A
        這個時候有一批貨物須要從K運到J,那麼它的運輸路線必定是:
        K->A->B->C->D->E->F->G->H->I->J
        因此後來火車就有了兩個頭,因而可知雙向鏈表的重要性!!!node


        雙向鏈表:在單鏈表結點上增添了一個指針域,指向當前結點的前驅。這樣就能夠方便的由其後繼來找到其前驅,而實現輸出終端結點到開始結點的數據序列。
這裏寫圖片描述web

雙向鏈表的結點結構svg

/// <summary>
/// 雙向鏈表節點
/// </summary>
/// <typeparam name="T"></typeparam>
public class BdNode<T>
{
    public T data { set; get; }
    public BdNode<T> next { set; get; }
    public BdNode<T> prev { set; get; }
    public BdNode(T val, BdNode<T> prev, BdNode<T> next)
    {
        this.Data = val;
        this.Prev = prev;
        this.Next = next;
    }
    public BdNode(T data, BdNode<T> next)
    {
        this.data = data;
        this.next = next;
        this.prev = null;
    }
    public BdNode(BdNode<T> next)
    {
        this.data = default(T);
        this.next = next;
        this.prev = null;
    }
    public BdNode(T data)
    {
        this.data = data;
        this.next = null;
        this.prev = null;
    }
    public BdNode()
    {
        data = default(T);
        next = null;
        prev = null;
    }
    public T Data
    {
        set { this.data = value; }
        get { return this.data; }
    }
    public BdNode<T> Prev
    {
        get { return prev; }
        set { prev = value; }
    }
    public BdNode<T> Next
    {
        get { return next; }
        set { next = value; }
    }
}

        

雙向鏈表還能做爲雙向循環鏈表使用,下圖爲雙向循環鏈表的結構圖:
這裏寫圖片描述
        this

雙向鏈表的插入:
        總共四個步驟:
        步驟1:a.next=a2;
        步驟2:a.prev=a1;
        步驟3:a1.next=a;
        步驟4:a2.prev=a;
        插入順序按圖標所示,操做雖然簡單可是不能寫反,順序很重要!
雙向鏈表的插入圖示
其實現代碼以下:spa

/// <summary>
/// 在最後附加元素
/// </summary>
/// <param name="item"></param>
public void Append(T item)
{
    BdNode<T> d = new BdNode<T>(item);
    BdNode<T> n = new BdNode<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;
    }
    //在最開頭插入
    if (i == 0)
    {
        BdNode<T> q = new BdNode<T>(item);
        q.Next = head;//把"頭"改爲第二個元素
        head.Prev = q;
        head = q;//把本身設置爲"頭"
        return;
    }
    BdNode<T> n = head;
    BdNode<T> d = new BdNode<T>();
    int j = 0;
    //找到位置i的前一個元素d
    while (n.Next != null && j < i)
    {
        d = n;
        n = n.Next;
        j++;
    }
    if (n.Next == null) //說明是在最後節點插入(即追加)
    {
        BdNode<T> q = new BdNode<T>(item);
        n.Next = q;
        q.Prev = n;
        q.Next = null;
    }
    else
    {
        if (j == i)
        {
            BdNode<T> q = new BdNode<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)
    {
        BdNode<T> q = new BdNode<T>(item);
        q.Next = head.Next;
        head.Next.Prev = q;
        head.Next = q;
        q.Prev = head;
        return;
    }
    BdNode<T> p = head;
    int j = 0;
    while (p != null && j < i)
    {
        p = p.Next;
        j++;
    }
    if (j == i)
    {
        BdNode<T> q = new BdNode<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!");
    }
}

刪除操做:只需把被刪除節點的前一個節點的next指針指向它下一個節點的下一個節點,再把被刪除節點的下一個節點的prev指針指向它前一個節點的前一個節點就OK了,以下圖所示:
刪除操做圖示
其實現代碼以下:指針

/// <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);
    }
    BdNode<T> q = new BdNode<T>();
    if (i == 0)
    {
        q = head;
        head = head.Next;
        head.Prev = null;
        return q.Data;
    }
    BdNode<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);
    }
}

        
雙向鏈表的好處在於:
         一、對鏈表數據的遍歷操做不單單能向後遍歷(如單鏈表),並且還可以向前遍歷尋找元素,對鏈表數據的操做更加靈活。
         二、能夠直接刪除某一個數據元素,我的認爲這是比較重要的一方面,由於對單鏈表而言,若是要刪除某一個數據元素,須要遍歷至此元素以前的一個結點才能刪除,而雙向鏈表能夠遍歷到某一元素,而後能夠直接對它進行刪除操做。code

注:.Net中微軟已經給出了一個內置的雙向鏈表System.Collections.Generic.LinkedList< T >,在瞭解雙鏈表的原理後,建議你們直接系統內置的鏈表。