链表剖析之双向链表剖析

来源:互联网 发布:西门子plm软件主要功能 编辑:程序博客网 时间:2024/05/29 19:21

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

如图

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

链表节点结构如下

    

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


结果如图:

2 0
原创粉丝点击