数据结构之双向链表

        双向链表宛如一列火车,刚发明的时候只有一个头,如果它的行驶路线为: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
        所以后来火车就有了两个头,由此可见双向链表的重要性!!!


        双向链表:在单链表结点上增添了一个指针域,指向当前结点的前驱。这样就可以方便的由其后继来找到其前驱,而实现输出终端结点到开始结点的数据序列。
这里写图片描述

双向链表的结点结构

/// <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; }
    }
}

        

双向链表还能作为双向循环链表使用,下图为双向循环链表的结构图:
这里写图片描述
        

双向链表的插入:
        总共四个步骤:
        步骤1:a.next=a2;
        步骤2:a.prev=a1;
        步骤3:a1.next=a;
        步骤4:a2.prev=a;
        插入顺序按图标所示,操作虽然简单但是不能写反,顺序很重要!
双向链表的插入图示
其实现代码如下:

/// <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);
    }
}

        
双向链表的好处在于:
         1、对链表数据的遍历操作不仅仅能向后遍历(如单链表),而且还能够向前遍历寻找元素,对链表数据的操作更加灵活。
         2、可以直接删除某一个数据元素,个人认为这是比较重要的一方面,因为对单链表而言,如果要删除某一个数据元素,需要遍历至此元素之前的一个结点才能删除,而双向链表可以遍历到某一元素,然后可以直接对它进行删除操作。

注:.Net中微软已经给出了一个内置的双向链表System.Collections.Generic.LinkedList< T >,在了解双链表的原理后,建议大家直接系统内置的链表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值