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