链表剖析之双向链表剖析

双向链表 和单链表不同的是每个节点增加了一个前驱

如图

这里我暂时 不涉及到循环链表

链表节点结构如下

    

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 双向链表
{
    public class MyDulListNode<T>
    {

        public MyDulListNode()
        { 
        
        }

        public MyDulListNode(T elem)
        {
            data = elem;
        }

        public T data;
        //双向链表的下一个节点
        public MyDulListNode<T> next;
        //双向链表的上一个节点
        public MyDulListNode<T> prev;

    }
}


 

也就是增加了一个前驱

操作双向链表的难点就是操作他的节点 查询和修改好说而增加和删除 要移动的节点:稍微复杂一点,这里给出增加和删除移动节点图

这张是删除的

 

 

 

 

这张是增加的

 

根据图片移动节点 双向链表代码如下代码依旧是用c#写的如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 双向链表
{
    public class MyDulList<T>
    {
        private int count;
        //双向链表的长度
        public int Count
        {
            get { return count; }
        }
        //头节点 头节点只是为了获取第一个节点
        MyDulListNode<T> head;
        public MyDulList()
        {
            head = new MyDulListNode<T>();
            head.next = null;
            head.prev = null;
        }
        public MyDulList(T[] nums):this()
        {
            AddItem(nums);
        }
        /// <summary>
        /// 增加一组数组到链表里
        /// </summary>
        /// <param name="nums"></param>
        private void AddItem(T[] nums)
        {
            foreach (T item in nums)
            {
                AddObject(item);
            }
        }
        /// <summary>
        /// 把一个节点加到头节点后面
        /// </summary>
        /// <param name="item"></param>
        private void AddObject(T item)
        {
            //这里给单链表开辟了表长的空间
            MyDulListNode<T> p = new MyDulListNode<T>();
            p.data = item;
            //设置原来的父节点的子节点为 现子节点的父节点
            p.next = head.next;
            //第一次设置的时候p的下一个节点是null所以不用设置 第三个节点与第二个节点的prev关系
            if (p.next != null)
            {
                //设置原来父节点的子节点的父节点为p
                p.next.prev = p;   //head.next = p也OK
            }
          
            //设置头节点的的下一个节点为p
            head.next = p;
            count++;
        }
        /// <summary>
        /// 向指定的索引插入一个数据
        /// </summary>
        /// <param name="index">位置</param>
        /// <param name="elem">数据值</param>
        public void InsertElem(int index,T elem)
        {
            MyDulListNode<T> p = head;
            int j = 0;
            //可以插入到最后的位置所以索引可以等于长度
            if (index < 0 || index > count)
            {
                throw new Exception("插入的索引超界");
            }
            //找到插入点的前一个
            while (p != null && index > j)
            {
                p = p.next;
                j++;
            }
            MyDulListNode<T> newnode = new MyDulListNode<T>(elem);
            //新节点的下一个节点是旧节点的下一个节点
            newnode.next = p.next;
            //旧节点的下一个节点的上一个节点是新节点
            newnode.next.prev = newnode;  //这里也可以写p.next
            //旧节点的下一个节点是新节点
            p.next = newnode;
            //头节点木有前驱
            if (index != 0)
            {
                //如果不是头节点新节点的上一个节点是旧节点
                newnode.prev = p;     
            }
            //插入节点后链表长度加1
            count++;
        }
        /// <summary>
        /// 删除某个节点
        /// </summary>
        /// <param name="index"></param>
        public void DeleteElem(int index)
        {
            MyDulListNode<T> p = head;
            int j = 0;
            //最后一位只能取到count-1,所以相等的时候也超界
            if (index < 0 || index >= count)
            {
                throw new Exception("删除索引超界");
            }
            //获取到删除的前一个节点
            while (p != null && index > j)
            {
                p = p.next;
                j++;
            }
            //设置删除的节点的上一个节点与下一个节点的关系
            p.next = p.next.next;
            //如果删除的是最后一个节点不用设置后一个节点的前驱说
            if (p.next != null)
            {
                p.next.prev = p;
            }
            //此时原来的节点已经没有引用了 会被GC自动回收
            count--;

        }
        /// <summary>
        /// 获取双向链表中指定索引的元素
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public T GetElem(int index)
        {
            MyDulListNode<T> p = head.next;
            int j = 0;
            while (p != null && index > j)
            {
                p = p.next;
                j++;
            }
            if (p == null || j > index)
            {
                throw new Exception("超出界限");
            }
            return p.data;
        }
        /// <summary>
        /// 修改双向链表中指定索引的元素的值
        /// </summary>
        /// <param name="index"></param>
        /// <param name="elem"></param>
        public void UpDateElem(int index, T elem)
        {
            MyDulListNode<T> p = head;
            // =的情况也不行因为链表最多取到count - 1
            if (index >= count)
            {
                throw new Exception("删除的索引超界");
            }
            int j = 0;
            //定位要要修改的位置
            while (index >= j)
            {
                p = p.next;
                j++;
            }
            p.data = elem;
        }

        /// <summary>
        /// 重载ToString输出双向链表
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            MyDulListNode<T> p = head.next;
            while (p != null)
            {
                sb.Append(p.data + " ");
                p = p.next;
            }
            return sb.ToString();
        }
        
    }
}


 

测试代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 双向链表
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请输入测试数组类似5 4 3");
            string[] s = Console.ReadLine().Split(' ');
            int[] nums = new int[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                nums[i] = Convert.ToInt32(s[i]);
            }
            //初始化的时候存的类似于 3=>4=>5
            MyDulList<int> ml = new MyDulList<int>(nums);
            Console.WriteLine("双向链表输出的结果是");
            Console.WriteLine(ml.ToString());
            Console.WriteLine("请输入插入节点的索引");
            int indexInsert = int.Parse(Console.ReadLine());
            Console.WriteLine("请输入插入节点的值");
            int valueInsert = int.Parse(Console.ReadLine());
            ml.InsertElem(indexInsert, valueInsert);
            Console.WriteLine("双向链表输出的结果是");
            Console.WriteLine(ml.ToString());
            Console.WriteLine("请输入删除节点的索引");
            int indexDel = int.Parse(Console.ReadLine());
            ml.DeleteElem(indexDel);
            Console.WriteLine("双向链表输出的结果是");
            Console.WriteLine(ml.ToString());
            Console.WriteLine("请输入你想获取的节点索引");
            int index = int.Parse(Console.ReadLine());
            Console.WriteLine(ml.GetElem(index));
            Console.WriteLine("请输入要修改的索引");
            int indexUpdate = int.Parse(Console.ReadLine());
            Console.WriteLine("请输入要修改的值");
            int valueUpdate = int.Parse(Console.ReadLine());
            ml.UpDateElem(indexUpdate, valueUpdate);
            Console.WriteLine("双向链表输出的结果是");
            Console.WriteLine(ml.ToString());
            Console.ReadLine();
        }
    }
}


结果如图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值