双向链表 和单链表不同的是每个节点增加了一个前驱
如图
这里我暂时 不涉及到循环链表
链表节点结构如下
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(); } } }
结果如图: