zoukankan      html  css  js  c++  java
  • 链表(二):双向链表

    一、双向链表

    单向链表和环形链表都是属于拥有方向性的链表,只能单向遍历,万一不幸其中有一个链接断裂,那么后面的链表数据便会遗失而无法复原了。因此,我们可以将两个方向不同的链表结合起来,除了存放数据的字段以外,它还有两个指针变量,其中一个指针指向后面的节点,另一个则指向前面的节点,这样的链表被称为双向链表(Double Linked List)。

    由于每个节点都有两个指针,可以双向通行,因此能够轻松地找到前后节点,同时从链表中任意的节点也可以找到其他节点,而不需要经过反转或对比节点等处理,执行速度较快。另外,如果任一节点的链接断裂,可通过反方向链表进行遍历,从而快速地重建完整的链表。

    双向链表的最大优点是有两个指针分别指向节点前后两个节点,所以能够轻松地找到前后节点,同时从双向链表中任一节点也可以找到其他节点,而不需要经过反转或对比节点等处理,执行速度较快。

    双向链表的缺点是由于双向链表有两个链接,所以在加入或删除节点时都要花更多时间来调整指针。另外因为每个节点含有两个指针变量,所以较浪费空间。

    二、双向链表的定义

    下面来介绍双向链表的数据结构。对每个节点而言,具有三个字段,中间为数据字段,左右两边各有两个链表字段,分表为LLink和RLink,其中LLink指向前一个节点,RLink指向后一个节点,如图所示:

     

    在双向链表中,通常加上一个链表头,该链表节点不存放任何数据,其左链接字段指向链表的最后一个节点,而右链接指向第一个节点。

    如果使用C#语言来声明双向链表节点的数据结构,那么其声明的程序代码如下:

    namespace DoubleLinkedListDemo
    {
        public class Node
        {
            /// <summary>
            /// 数据字段
            /// </summary>
            public int Data { get; set; }
    
            /// <summary>
            /// 后续节点
            /// </summary>
            public Node NextNode { get; set; }
    
            /// <summary>
            /// 前置节点
            /// </summary>
            public Node PreNode { get; set; }
    
            public Node(int data)
            {
                Data = data;
                NextNode = null;
                PreNode = null;
            }
        }
    }

    1、双向链表节点的插入

     双向链表节点的插入有下面三种情况。

    1、新节点插入到链表的第一个节点前

    将新节点插入到链表的第一个节点前分为如下的步骤:

    1. 将新节点的右指针指向原链表的第一个节点。
    2. 将原链表第一个节点的左指针指向新节点。
    3. 将原链表的表头指针指向新节点,且新节点的左指针指向null。

    如图所示:

    2、新节点插入此链表的末尾

    将新节点插入到此链接的末尾分为如下步骤:

    1. 将原链表的最后一个节点的右指针指向新节点。
    2. 将新节点的左指针指向原链表的最后一个节点,并将新节点的右指针指向null。

    如图所示:

    3、新节点插入到中间节点

    将新节点插入到中间节点(ptr指向的节点)之后,分为如下步骤:

    1. 将ptr节点的右指针指向新节点。
    2. 将新节点的左指针指向ptr节点。
    3. 将ptr节点的下一个节点的左指针指向新节点。
    4. 将新节点的右指针指向ptr的下一个节点。

    如图所示:

    2、双向链表节点的删除

    双向链表的节点删除,同样也有下面的三种情况。

    1、删除双向链表的第一个节点

    将双向链表的第一个节点删除分为如下步骤:

    1. 将链表头指针head指向原链表的第二个节点。
    2. 将新的链表头指针指向null。

    如图所示:

    2、删除双向链表的最后一个节点

    删除双向链表的最后一个节点只需要将原链表最后一个节点的前一个节点的右指针指向null即可,如图所示:

    3、删除双向链表的中间节点

    假设ptr指向的是双向链表的中间节点,删除该节点,分为如下步骤:

    1. 将ptr节点的前一个节点的右指针指向ptr节点的下一个节点。
    2. 将ptr节点的下一个节点的左指针指向ptr节点的前一个节点。

    如图所示:

    下面我们以具体的代码演示双向链表的数据结构、创立、插入和删除节点,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DoubleLinkedListDemo
    {
        /// <summary>
        /// 双向链表类
        /// </summary>
        public class DoubleLinkedList
        {
            /// <summary>
            /// 第一个节点
            /// </summary>
            public Node HeadNode;
    
            /// <summary>
            /// 尾节点
            /// </summary>
            public Node LastNode;
    
            /// <summary>
            /// 判断当前双向链表是否为空链表
            /// </summary>
            /// <returns></returns>
            public bool IsEmpty()
            {
                return HeadNode == null;
            }
    
            /// <summary>
            /// 打印出节点的数据字段
            /// </summary>
            public void Print()
            {
                Node currentNode = HeadNode;
                while(currentNode!=null)
                {
                    Console.Write($"{currentNode.Data}  ");
                    currentNode = currentNode.NextNode;
                }
                Console.WriteLine();
            }
    
            /// <summary>
            /// 新增节点,用来创建一个双向链表,每次都是插入到最后一个节点之后
            /// </summary>
            /// <param name="node"></param>
            public void AddNode(Node newNode)
            {
                if(HeadNode==null)
                {
                    HeadNode = newNode;
                    LastNode = newNode;
                    HeadNode.NextNode = LastNode;
                    LastNode.PreNode = HeadNode;
                    LastNode.NextNode = null;
                }
                else
                {
                    LastNode.NextNode = newNode;
                    newNode.PreNode = LastNode;
                    LastNode = newNode;
                    //Node temp = HeadNode;
                    //// 循环找到最后一个节点,然后添加
                    //// 退出循环时temp就是最后一个节点
                    //while(true)
                    //{
                    //    if(temp.NextNode!=null)
                    //    {
                    //        // 后移节点
                    //        temp = temp.NextNode;
                    //    }
                    //    else
                    //    {
                    //        // 退出循环
                    //        break;
                    //    }
                    //}
                    //// 将新节点插入到最后一个节点之后
                    //temp.NextNode = newNode;
                    //newNode.PreNode = temp;
                    //
                    // LastNode = newNode;
                }
            }
    
            public void InsertNode(int item,int index)
            {
                Node newNode = new Node(item);
                // 插入头部位置
                if(index==0)
                {
                    HeadNode.PreNode = newNode;
                    newNode.NextNode = HeadNode;
                    HeadNode = newNode;
                }
                else if(index == GetLinkedList()-1)
                {
                    // 插入尾部位置
                    LastNode.NextNode = newNode;
                    newNode.PreNode = LastNode;
                    LastNode = newNode;
                }
                else
                {
                    // tempNode就是要插入的节点位置
                    Node tempNode = HeadNode;
                    // 插入中间位置
                    for (int i = 0; i < GetLinkedList()-1; i++)
                    {
                        if(i!=index)
                        {
                            tempNode = tempNode.NextNode;
                        }
                        else
                        {
                            break;
                        }
                    }
                    newNode.PreNode = tempNode;
                    newNode.NextNode = tempNode.NextNode;
                    tempNode.NextNode.PreNode = newNode;
                    tempNode.NextNode = newNode;
                }
            }
    
            /// <summary>
            /// 获取链表长度
            /// </summary>
            /// <returns></returns>
            public int GetLinkedList()
            {
                int length = 0;
                if(HeadNode==null)
                {
                    length = 0;
                }
                else
                {
                    Node tempNode = HeadNode;
                    while(true)
                    {
                        if(tempNode.NextNode!=null)
                        {
                            tempNode = tempNode.NextNode;
                            length++;
                        }
                        else
                        {
                            length++;
                            break;
                        }
                    }
                }
                return length;
            }
    
    
        }
    }

    Main方法中调用:

    using System;
    
    namespace DoubleLinkedListDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                Node node1 = new Node(1);
                Node node2 = new Node(34);
                Node node3 = new Node(564);
                Node node4 = new Node(81);
                Node node5 = new Node(6);
    
                DoubleLinkedList dLinkedList = new DoubleLinkedList();
                dLinkedList.AddNode(node1);
                dLinkedList.AddNode(node2);
                dLinkedList.AddNode(node3);
                dLinkedList.AddNode(node4);
                dLinkedList.AddNode(node5);
                Console.WriteLine("插入前链表");
                dLinkedList.Print();
                // 插入头部
                dLinkedList.InsertNode(29, 0);
                Console.WriteLine("插入头部后链表");
                dLinkedList.Print();
                // 插入尾部
                dLinkedList.InsertNode(724, dLinkedList.GetLinkedList() - 1);
                Console.WriteLine("插入尾部后链表");
                dLinkedList.Print();
                // 插入中间节点
                int index = new Random().Next(1, dLinkedList.GetLinkedList());
                dLinkedList.InsertNode(34242, index);
                Console.WriteLine("插入中间位置后链表");
                dLinkedList.Print();
                Console.ReadKey();
            }
        }
    }

    程序运行结果:

     

  • 相关阅读:
    原生JS里获取class属性
    在Aptana下安装Zen coding
    一个Vim配置
    在Aptana下安装Zen coding
    Sublime Text2破解
    评价。评星级js代码
    javascript 6步搞定性能优化!
    document.getElementById的简写方式
    aptana 代码折行
    vim的代码折叠:设置默认代码不折叠
  • 原文地址:https://www.cnblogs.com/dotnet261010/p/12316809.html
Copyright © 2011-2022 走看看