单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。
链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据
MyLinkedListNode:自定义链表节点类
/// <summary>
/// 自定义链表节点类: 单链表
/// </summary>
public class MyLinkedListNode<T>
{
/// <summary>
/// 当前节点
/// </summary>
public T Node { get; set; }
/// <summary>
/// 下一个节点
/// </summary>
public MyLinkedListNode<T> Next { get; set; }
/// <summary>
/// 构造函数: 无参构造函数
/// </summary>
/// <param name="Node"></param>
public MyLinkedListNode()
{
this.Node = default;
this.Next = null;
}
/// <summary>
/// 构造函数: 指定当前节点,常用于 新增根节点和最后一个节点
/// </summary>
/// <param name="node"></param>
public MyLinkedListNode(T node)
{
this.Node = node;
this.Next = null;
}
/// <summary>
/// 构造函数: 指定当前节点和下一节点,常用于 新增内部节点/确定下一节点 的情况
/// </summary>
/// <param name="next"></param>
/// <param name="Next"></param>
public MyLinkedListNode(T node, MyLinkedListNode<T> next)
{
this.Node = node;
this.Next = next;
}
}
MyLinkedList:自定义链表
/// <summary>
/// 自定义链表
/// 功能:
/// 1.添加: 添加到集合最后面
/// 2.添加: 添加到集合最前面
/// 3.添加: 添加索引后面
/// 4.添加: 添加索引前面
/// 5.删除: 删除T
/// 6.删除: 删除指定索引
/// 7.删除: 删除第一个
/// 8.删除: 删除最后一个
/// 9.删除: 删除所有
/// </summary>
public class MyLinkedList<T>
{
/// <summary>
/// 存储链表集合-根节点:
/// 框架自带了双向链表 System.Collections.Generic.LinkedList,链表集合保存在 MyLinkedListNode 中
/// 考虑到存在clear方法,链表默认值为null更方便
/// </summary>
private MyLinkedListNode<T> \_rootNode = null; // { get; set; }
/// <summary>
/// 索引索引器,从0开始,根据索引返回指定索引节点信息
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T this\[int index\]
{
get
{
var node = GetNodeAt(index).Node;
Console.WriteLine($"this\[int {index}\] = {node}\\r\\n");
return node;
}
}
#region 查询方法
/// <summary>
/// 根据索引返回指定索引节点信息
/// </summary>
/// <param name="index">索引,从0开始</param>
/// <returns></returns>
private MyLinkedListNode<T> GetNodeAt(int index) => GetNodeTupleAt(index)?.Item1;
/// <summary>
/// 根据索引返回指定索引:节点元组
/// </summary>
/// <param name="index">索引,从0开始</param>
/// <returns>item1:当前节点;item2:上一节点;item3:下一节点;</returns>
private Tuple<MyLinkedListNode<T>, MyLinkedListNode<T>, MyLinkedListNode<T>> GetNodeTupleAt(int index)
{
if (index < ) return null;
if (\_rootNode == null) throw new Exception("自定义链表为空!");
var num = ;
// 当前节点
MyLinkedListNode<T> currentNode = \_rootNode;
// 上一节点
MyLinkedListNode<T> prevNode = \_rootNode;
// while循环会在 currentNode == 倒数第二个节点时就会停止循环,所以while后面需要再做一次判断
while (currentNode.Next != null)
{
// 如果当前索引 和 查找索引相同,则返回担负起节点
if (num == index) return GetValidNodeTuple(index, currentNode, prevNode);
// 重置:上一节点
prevNode = currentNode;
// 重置:当前节点
currentNode = currentNode.Next;
num++;
}
if (num < index) throw new Exception("索引超过链表长度!");
return num == index ? GetValidNodeTuple(index, currentNode, prevNode) : null;
}
/// <summary>
/// 获取有效的节点元组
/// </summary>
/// <param name="index">索引</param>
/// <param name="currentNode">当前节点</param>
/// <param name="prevNode">上一节点【如果索引 == 0 ? null :上一节点 】</param>
/// <returns>item1:当前节点;item2:上一节点;item3:下一节点;</returns>
private Tuple<MyLinkedListNode<T>, MyLinkedListNode<T>, MyLinkedListNode<T>> GetValidNodeTuple(int index, MyLinkedListNode<T> currentNode, MyLinkedListNode<T> prevNode)
{
return new Tuple<MyLinkedListNode<T>, MyLinkedListNode<T>, MyLinkedListNode<T>>(currentNode, index == ? null : prevNode, currentNode.Next);
}
#endregion
#region 添加方法
/// <summary>
/// 1.添加: 添加到集合最后面
/// </summary>
/// <param name="item"></param>
public void Append(T item)
{
MyLinkedListNode<T> node = new MyLinkedListNode<T>(item);
// 如果链表集合为空,则讲当前 元素当作跟节点
if (\_rootNode == null)
{
\_rootNode = node;
return;
}
// 循环得到最末节点
MyLinkedListNode<T> currentNode = \_rootNode;
while (currentNode.Next != null) currentNode = currentNode.Next;
// 添加到集合最后面
currentNode.Next = node;
}
/// <summary>
/// 2.添加: 添加到集合最前面
/// </summary>
/// <param name="item"></param>
public void AddFirst(T item)
{
MyLinkedListNode<T> node = new MyLinkedListNode<T>(item);
// 如果链表集合为空,则讲当前 元素当作跟节点
if (\_rootNode == null)
{
\_rootNode = node;
return;
}
\_rootNode = new MyLinkedListNode<T>(item, \_rootNode);
// 显示链表中的所有数据
Console.Write($"AddFirst({item})\\t");
Show();
}
/// <summary>
/// 3.添加: 在索引后面添加
/// 3.1.获取到当前索引的节点
/// 3.2.根据item创建新节点,把 当前节点的 下一节点指给 新节点的下一节点
/// 3.3.把新节点当作当前节点的下一节点
/// </summary>
/// <param name="index"></param>
/// <param name="item"></param>
public void AddAtAfter(int index, T item)
{
MyLinkedListNode<T> node = new MyLinkedListNode<T>(item);
// 如果链表集合为空,则讲当前 元素当作跟节点
if (\_rootNode == null)
{
\_rootNode = node;
return;
}
// 3.1.获取到当前索引的节点
var currentNode = GetNodeAt(index);
// 如果链表集合为空,则讲当前 元素当作跟节点
if (currentNode == null)
{
\_rootNode = node;
return;
}
// 3.2.根据item创建新节点
var newNode = new MyLinkedListNode<T>(item, currentNode.Next);
// 3.3.把新节点当作当前节点的下一节点
currentNode.Next = newNode;
// 显示链表中的所有数据
Console.Write($"AddAtAfter(int {index},T {item})\\t");
Show();
}
/// <summary>
/// 4.添加: 在索引前面添加
/// 4.1.获取到 当前索引 和 上一索引 的节点
/// 4.2.根据item创建新节点,把当前节点当作新节点的下一节点
/// 4.3.把新节点当作上一节点的下一节点
/// </summary>
/// <param name="index"></param>
/// <param name="item"></param>
public void AddAtBefore(int index, T item)
{
var nodeTuple = GetNodeTupleAt(index);
if (nodeTuple == null) throw new Exception("索引超过链表长度!");
// 4.1.获取到 当前索引 和 上一索引 的节点
var currentNode = nodeTuple.Item1;
var prevtNode = nodeTuple.Item2;
// 4.2.根据item创建新节点,把当前节点当作新节点的下一节点
var newNode = new MyLinkedListNode<T>(item, currentNode);
// 4.3.把新节点当作上一节点的下一节点:如果索引是0,则新节点作为链接根节点
if (index == ) \_rootNode = newNode;
else prevtNode.Next = newNode;
// 显示链表中的所有数据
Console.Write($"AddAtBefore(int {index},T {item})\\t");
Show();
}
#endregion
#region 删除方法
/// <summary>
/// 5.删除: 删除T
/// 5.1.得到 当前节点/上一节点/下一节点
/// 5.2.把 上一节点的下一节点 更新为 下一节点
/// </summary>
/// <param name="item"></param>
public void Remove(T item)
{
if (\_rootNode == null) return;
// 当前节点
var currentNode = \_rootNode;
// 上一节点
MyLinkedListNode<T> prevNode = null;
while (currentNode.Next != null)
{
if (currentNode.Node.Equals(item))
{
// 根据 当前节点 的 上一节点和下一节点 删除 当前节点
Remove(prevNode, currentNode.Next);
// 显示链表中的所有数据
Console.Write($"Remove({item})\\t");
Show();
return;
}
// 重置 上一节点 和 当前节点
prevNode = currentNode;
currentNode = currentNode.Next;
}
// 如果需要删除的是最后一个节点,则while循环在 currentNode == 倒数第二个节点时就会停止循环
Remove(prevNode, null);
// 显示链表中的所有数据
Console.Write($"Remove({item})\\t");
Show();
}
/// <summary>
/// 根据 当前节点 的 上一节点和下一节点 删除 当前节点:把上一节点的next 指向 下一节点
/// </summary>
/// <param name="prevNode"></param>
/// <param name="nextNode"></param>
private void Remove(MyLinkedListNode<T> prevNode, MyLinkedListNode<T> nextNode)
{
if (prevNode == null) \_rootNode = nextNode;
else prevNode.Next = nextNode;
}
/// <summary>
/// 6.删除: 删除指定索引
/// 6.1.得到 当前/上一/下一节点
/// 6.2.把当前节点的下一节点 更新为 下一节点
/// </summary>
/// <param name="index"></param>
public void RemoveAt(int index)
{
var nodeTuple = GetNodeTupleAt(index);
// 上一节点
var prevNode = nodeTuple.Item2;
// 判断上一节点是不是null ? 当前节点为根节点,需要把下一节点更新为更节点
if (prevNode == null) \_rootNode = nodeTuple.Item3;
else prevNode.Next = nodeTuple.Item3;
// 显示链表中的所有数据
Console.Write($"RemoveAt({index})\\t");
Show();
}
/// <summary>
/// 7.删除: 删除第一个
/// </summary>
public void RemoveFirst()
{
if (\_rootNode == null) return;
\_rootNode = \_rootNode.Next;
// 显示链表中的所有数据
Console.Write($"RemoveFirst()\\t");
Show();
}
/// <summary>
/// 8.删除: 删除最后一个
/// </summary>
public void RemoveLast()
{
if (\_rootNode == null) return;
// 如果链表只存在根节点,则把根节点删除
if (\_rootNode.Next == null)
{
\_rootNode = null;
return;
}
// while循环获得最后一个节点
var currentNode = \_rootNode;
// 上一节点/倒数第二个节点
MyLinkedListNode<T> prevNode = null;
while (currentNode.Next != null)
{
prevNode = currentNode;
currentNode = currentNode.Next;
}
prevNode.Next = null;
// 显示链表中的所有数据
Console.Write($"RemoveLast()\\t");
Show();
}
/// <summary>
/// 9.删除: 删除所有
/// </summary>
public void Clear()
{
\_rootNode = null;
// 显示链表中的所有数据
Console.Write($"Clear()\\t");
Show();
}
#endregion
/// <summary>
/// 显示链表中的所有数据
/// </summary>
public void Show()
{
if (\_rootNode == null)
{
Console.WriteLine($"链表中的数据为空!\\r\\n");
return;
}
StringBuilder builder = new StringBuilder();
MyLinkedListNode<T> currentNode = \_rootNode;
while (currentNode.Next != null)
{
builder.Append($"{currentNode.Node}\\t");
currentNode = currentNode.Next;
}
// 最后一个节点next为null,不会进入while
builder.Append($"{currentNode.Node}\\t");
Console.WriteLine($"链表中的数据为:\\r\\n{builder.ToString()}\\r\\n");
}
}
测试代码:
/// <summary>
/// 测试单链表
/// </summary>
public static void RunLinkedList()
{
Console.WriteLine("======= 链表测试 Start =======");
MyLinkedList<string> myLinkedList = new MyLinkedList<string>();
myLinkedList.Append("张三");
myLinkedList.Append("李四");
myLinkedList.Append("王五");
myLinkedList.Append("六麻子");
myLinkedList.Append("田七");
myLinkedList.Show(); // 张三 李四 王五 六麻子 田七
// 异常测试
var a = myLinkedList\[\]; // 张三
a = myLinkedList\[\]; // 六麻子
//a = myLinkedList\[11\];
// 测试添加功能
myLinkedList.AddFirst("郭大爷"); // 郭大爷 张三 李四 王五 六麻子 田七
myLinkedList.AddAtAfter(, "海大爷"); // 郭大爷 海大爷 张三 李四 王五 六麻子 田七
myLinkedList.AddAtBefore(, "Robot"); // Robot 郭大爷 海大爷 张三 李四 王五 六麻子 田七
myLinkedList.AddAtBefore(, "Robot"); // Robot 郭大爷 Robot 海大爷 张三 李四 王五 六麻子 田七
myLinkedList.AddAtBefore(, "Robot"); // Robot 郭大爷 Robot 海大爷 Robot 张三 李四 王五 六麻子 田七
// 测试删除功能
myLinkedList.Remove("Robot"); // 郭大爷 Robot 海大爷 Robot 张三 李四 王五 六麻子 田七
myLinkedList.Remove("Robot"); // 郭大爷 海大爷 Robot 张三 李四 王五 六麻子 田七
myLinkedList.Remove("田七"); // 郭大爷 海大爷 Robot 张三 李四 王五 六麻子
myLinkedList.RemoveAt(); // 海大爷 Robot 张三 李四 王五 六麻子
myLinkedList.RemoveAt(); // 海大爷 张三 李四 王五 六麻子
myLinkedList.RemoveFirst(); // 张三 李四 王五 六麻子
myLinkedList.RemoveFirst(); // 李四 王五 六麻子
myLinkedList.RemoveLast(); // 李四 王五
myLinkedList.RemoveLast(); // 李四
myLinkedList.Clear(); // 链表中的数据为空!
Console.WriteLine("======= 链表测试 End =======");
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章