NET 数据结构-单链表
概念介绍:
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。
链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据
由图可知:
- 链表在进行添加/删除时,只需要修改 当前节点和相邻节点 的next,更新效率高
- 遍历数据,需要根据节点按顺序访问,导致查询速度慢,时间复杂度为O(n)
- 每个节点中都保存了下一个节点的指针【Next,最后一个节点的next为null】,所以长度是动态变化的,且不是连续内存空间
相关代码:
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 =======");
}
NET 数据结构-单链表的更多相关文章
- python实现数据结构单链表
#python实现数据结构单链表 # -*- coding: utf-8 -*- class Node(object): """节点""" ...
- C语言数据结构-单链表的实现-初始化、销毁、长度、查找、前驱、后继、插入、删除、显示操作
1.数据结构-单链表的实现-C语言 typedef struct LNode { int data; struct LNode* next; } LNode,*LinkList; //这两者等价.Li ...
- 数据结构——单链表java简易实现
巩固数据结构 单链表java实现 单链表除了表尾 每个几点都有一个后继 结点有数据和后继指针组成 通过构建表头和表尾(尾部追加需要)两个特殊几点 实现单链表的一些操作,代码如下 package co ...
- C# 数据结构--单链表
什么是单链表 这两天看到很多有关单链表的面试题,对单链表都不知道是啥的我.经过学习和整理来分享一下啥是单链表和单链表的一些基本使用方法.最后看些网上有关单链表的面试题代码实例. 啥是单链表? 单链表是 ...
- 数据结构-------单链表(C++)
相关信息: /** * @subject 数据结构 实验2 * @author 信管1142班 201411671210 赖俊杰 * @project 单链表 * @time 2015年10月29日1 ...
- C# 数据结构 - 单链表 双链表 环形链表
链表特点(单链表 双链表) 优点:插入和删除非常快.因为单链表只需要修改Next指向的节点,双链表只需要指向Next和Prev的节点就可以完成插入和删除操作. 缺点:当需要查找某一个节点的时候就需要一 ...
- 数据结构—单链表(类C语言描写叙述)
单链表 1.链接存储方法 链接方式存储的线性表简称为链表(Linked List). 链表的详细存储表示为: ① 用一组随意的存储单元来存放线性表的结点(这组存储单元既能够是连续的.也能够是不连续的) ...
- python算法与数据结构-单链表(38)
一.链表 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括 ...
- 数据结构——单链表(singly linked list)
/* singlyLinkedList.c */ /* 单链表 */ /* 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素. */ #include <stdio ...
- 数据结构-单链表-类定义2-C++
上一次的C++链表实现两个单链表的连接不太理想,此次听了一些视频课,自己补了个尾插法,很好的实现了两个链表的连接,当然了,我也是刚接触,可能是C++的一些语法还不太清楚,不过硬是花了一些时间尽量在数据 ...
随机推荐
- 从Point类继承的Circle类 代码参考
#include <iostream> #include <cstring> using namespace std; class Point { private: int x ...
- python3程序开发指南——第1章 笔记
python文件的扩展名为.py ,但是python GUI程序的扩展名为.pyw 在python中,注释以#开始,作用范围为该行 IDLE提供了三个关键功能:输入python表达式与代码,并在pyt ...
- Rocket - interrupts - Parameters
https://mp.weixin.qq.com/s/eD1_hG0n8W2Wodk25N5KnA 简单介绍interrupts相关的Parameters. 1. IntRange 定义一个中断号区间 ...
- Java实现 LeetCode 462 最少移动次数使数组元素相等 II
462. 最少移动次数使数组元素相等 II 给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1. 您可以假设数组的长度最多为10000. 例如: 输 ...
- Java实现蓝桥杯模拟组织晚会
问题描述 小明要组织一台晚会,总共准备了 n 个节目.然后晚会的时间有限,他只能最终选择其中的 m 个节目. 这 n 个节目是按照小明设想的顺序给定的,顺序不能改变. 小明发现,观众对于晚上的喜欢程度 ...
- Java实现 LeetCode 307 区域和检索 - 数组可修改
307. 区域和检索 - 数组可修改 给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点. update(i, val) 函数可以通过将下标 ...
- Java中BigDecimal类的常用方法
1.简介 BigDecimal类位于java.math.BigDecimal包下.使用此类可以完成大的小数操作,而且也可以使用此类进行精确的四舍五入,这一点在开发中经常使用. 对于不需要任何准确计算精 ...
- java实现第六届蓝桥杯打印大X
打印大X 打印大X 小明希望用星号拼凑,打印出一个大X,他要求能够控制笔画的宽度和整个字的高度. 为了便于比对空格,所有的空白位置都以句点符来代替. 要求输入两个整数m n,表示笔的宽度,X的高度.用 ...
- PAT 有几个PAT
字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位(P),第 4 位(A),第 6 位(T):第二个 PAT 是第 3 位(P),第 4 位(A),第 6 位(T). 现 ...
- CRC循环冗余校验---模2除法解析
关于模2除法,网上的人说了一大堆规则和原理,感觉很容易把小白绕进去,下面我只说怎么计算,不说其中的规则和原理.