zoukankan      html  css  js  c++  java
  • NET 数据结构-单链表

    概念介绍:

    单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素

    链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据

     由图可知:

    1. 链表在进行添加/删除时,只需要修改 当前节点和相邻节点 的next,更新效率高
    2. 遍历数据,需要根据节点按顺序访问,导致查询速度慢,时间复杂度为O(n)
    3. 每个节点中都保存了下一个节点的指针【Next,最后一个节点的next为null】,所以长度是动态变化的,且不是连续内存空间

    相关代码:

    MyLinkedListNode:自定义链表节点类

     1     /// <summary>
     2     /// 自定义链表节点类: 单链表
     3     /// </summary>
     4     public class MyLinkedListNode<T>
     5     {
     6         /// <summary>
     7         /// 当前节点
     8         /// </summary>
     9         public T Node { get; set; }
    10 
    11         /// <summary>
    12         /// 下一个节点
    13         /// </summary>
    14         public MyLinkedListNode<T> Next { get; set; }
    15 
    16         /// <summary>
    17         /// 构造函数: 无参构造函数
    18         /// </summary>
    19         /// <param name="Node"></param>
    20         public MyLinkedListNode()
    21         {
    22             this.Node = default;
    23             this.Next = null;
    24         }
    25 
    26         /// <summary>
    27         /// 构造函数: 指定当前节点,常用于 新增根节点和最后一个节点
    28         /// </summary>
    29         /// <param name="node"></param>
    30         public MyLinkedListNode(T node)
    31         {
    32             this.Node = node;
    33             this.Next = null;
    34         }
    35 
    36         /// <summary>
    37         /// 构造函数: 指定当前节点和下一节点,常用于 新增内部节点/确定下一节点 的情况
    38         /// </summary>
    39         /// <param name="next"></param>
    40         /// <param name="Next"></param>
    41         public MyLinkedListNode(T node, MyLinkedListNode<T> next)
    42         {
    43             this.Node = node;
    44             this.Next = next;
    45         }
    46     }
    View Code

    MyLinkedList:自定义链表

      1     /// <summary>
      2     /// 自定义链表
      3     /// 功能:
      4     ///     1.添加: 添加到集合最后面
      5     ///     2.添加: 添加到集合最前面
      6     ///     3.添加: 添加索引后面
      7     ///     4.添加: 添加索引前面
      8     ///     5.删除: 删除T
      9     ///     6.删除: 删除指定索引
     10     ///     7.删除: 删除第一个
     11     ///     8.删除: 删除最后一个
     12     ///     9.删除: 删除所有
     13     /// </summary>
     14     public class MyLinkedList<T>
     15     {
     16         /// <summary>
     17         /// 存储链表集合-根节点: 
     18         /// 框架自带了双向链表 System.Collections.Generic.LinkedList,链表集合保存在 MyLinkedListNode 中
     19         /// 考虑到存在clear方法,链表默认值为null更方便
     20         /// </summary>
     21         private MyLinkedListNode<T> _rootNode = null; // { get; set; }
     22 
     23         /// <summary>
     24         /// 索引索引器,从0开始,根据索引返回指定索引节点信息
     25         /// </summary>
     26         /// <param name="index"></param>
     27         /// <returns></returns>
     28         public T this[int index]
     29         {
     30             get
     31             {
     32                 var node = GetNodeAt(index).Node;
     33                 Console.WriteLine($"this[int {index}] = {node}
    ");
     34                 return node;
     35             }
     36         }
     37 
     38         #region 查询方法
     39 
     40         /// <summary>
     41         /// 根据索引返回指定索引节点信息
     42         /// </summary>
     43         /// <param name="index">索引,从0开始</param>
     44         /// <returns></returns>
     45         private MyLinkedListNode<T> GetNodeAt(int index) => GetNodeTupleAt(index)?.Item1;
     46 
     47         /// <summary>
     48         /// 根据索引返回指定索引:节点元组
     49         /// </summary>
     50         /// <param name="index">索引,从0开始</param>
     51         /// <returns>item1:当前节点;item2:上一节点;item3:下一节点;</returns>
     52         private Tuple<MyLinkedListNode<T>, MyLinkedListNode<T>, MyLinkedListNode<T>> GetNodeTupleAt(int index)
     53         {
     54             if (index < 0) return null;
     55             if (_rootNode == null) throw new Exception("自定义链表为空!");
     56 
     57             var num = 0;
     58             // 当前节点
     59             MyLinkedListNode<T> currentNode = _rootNode;
     60             // 上一节点
     61             MyLinkedListNode<T> prevNode = _rootNode;
     62             // while循环会在  currentNode == 倒数第二个节点时就会停止循环,所以while后面需要再做一次判断
     63             while (currentNode.Next != null)
     64             {
     65                 // 如果当前索引 和 查找索引相同,则返回担负起节点
     66                 if (num == index) return GetValidNodeTuple(index, currentNode, prevNode);
     67                 // 重置:上一节点
     68                 prevNode = currentNode;
     69                 // 重置:当前节点
     70                 currentNode = currentNode.Next;
     71                 num++;
     72             }
     73             if (num < index) throw new Exception("索引超过链表长度!");
     74             return num == index ? GetValidNodeTuple(index, currentNode, prevNode) : null;
     75         }
     76 
     77         /// <summary>
     78         /// 获取有效的节点元组
     79         /// </summary>
     80         /// <param name="index">索引</param>
     81         /// <param name="currentNode">当前节点</param>
     82         /// <param name="prevNode">上一节点【如果索引 == 0 ? null :上一节点 】</param>
     83         /// <returns>item1:当前节点;item2:上一节点;item3:下一节点;</returns>
     84         private Tuple<MyLinkedListNode<T>, MyLinkedListNode<T>, MyLinkedListNode<T>> GetValidNodeTuple(int index, MyLinkedListNode<T> currentNode, MyLinkedListNode<T> prevNode)
     85         {
     86             return new Tuple<MyLinkedListNode<T>, MyLinkedListNode<T>, MyLinkedListNode<T>>(currentNode, index == 0 ? null : prevNode, currentNode.Next);
     87         }
     88 
     89         #endregion
     90 
     91         #region 添加方法
     92 
     93         /// <summary>
     94         /// 1.添加: 添加到集合最后面
     95         /// </summary>
     96         /// <param name="item"></param>
     97         public void Append(T item)
     98         {
     99             MyLinkedListNode<T> node = new MyLinkedListNode<T>(item);
    100             // 如果链表集合为空,则讲当前 元素当作跟节点
    101             if (_rootNode == null)
    102             {
    103                 _rootNode = node;
    104                 return;
    105             }
    106 
    107             // 循环得到最末节点
    108             MyLinkedListNode<T> currentNode = _rootNode;
    109             while (currentNode.Next != null) currentNode = currentNode.Next;
    110 
    111             // 添加到集合最后面
    112             currentNode.Next = node;
    113         }
    114 
    115         /// <summary>
    116         /// 2.添加: 添加到集合最前面
    117         /// </summary>
    118         /// <param name="item"></param>
    119         public void AddFirst(T item)
    120         {
    121             MyLinkedListNode<T> node = new MyLinkedListNode<T>(item);
    122             // 如果链表集合为空,则讲当前 元素当作跟节点
    123             if (_rootNode == null)
    124             {
    125                 _rootNode = node;
    126                 return;
    127             }
    128 
    129             _rootNode = new MyLinkedListNode<T>(item, _rootNode);
    130 
    131             // 显示链表中的所有数据
    132             Console.Write($"AddFirst({item})	");
    133             Show();
    134         }
    135 
    136         /// <summary>
    137         /// 3.添加: 在索引后面添加
    138         /// 3.1.获取到当前索引的节点
    139         /// 3.2.根据item创建新节点,把 当前节点的 下一节点指给 新节点的下一节点
    140         /// 3.3.把新节点当作当前节点的下一节点
    141         /// </summary>
    142         /// <param name="index"></param>
    143         /// <param name="item"></param>
    144         public void AddAtAfter(int index, T item)
    145         {
    146             MyLinkedListNode<T> node = new MyLinkedListNode<T>(item);
    147             // 如果链表集合为空,则讲当前 元素当作跟节点
    148             if (_rootNode == null)
    149             {
    150                 _rootNode = node;
    151                 return;
    152             }
    153             // 3.1.获取到当前索引的节点
    154             var currentNode = GetNodeAt(index);
    155             // 如果链表集合为空,则讲当前 元素当作跟节点
    156             if (currentNode == null)
    157             {
    158                 _rootNode = node;
    159                 return;
    160             }
    161 
    162             // 3.2.根据item创建新节点
    163             var newNode = new MyLinkedListNode<T>(item, currentNode.Next);
    164 
    165             // 3.3.把新节点当作当前节点的下一节点
    166             currentNode.Next = newNode;
    167 
    168             // 显示链表中的所有数据
    169             Console.Write($"AddAtAfter(int {index},T {item})	");
    170             Show();
    171         }
    172 
    173         /// <summary>
    174         /// 4.添加: 在索引前面添加
    175         /// 4.1.获取到 当前索引 和 上一索引 的节点
    176         /// 4.2.根据item创建新节点,把当前节点当作新节点的下一节点
    177         /// 4.3.把新节点当作上一节点的下一节点
    178         /// </summary>
    179         /// <param name="index"></param>
    180         /// <param name="item"></param>
    181         public void AddAtBefore(int index, T item)
    182         {
    183             var nodeTuple = GetNodeTupleAt(index);
    184             if (nodeTuple == null) throw new Exception("索引超过链表长度!");
    185 
    186             // 4.1.获取到 当前索引 和 上一索引 的节点
    187             var currentNode = nodeTuple.Item1;
    188             var prevtNode = nodeTuple.Item2;
    189 
    190             // 4.2.根据item创建新节点,把当前节点当作新节点的下一节点
    191             var newNode = new MyLinkedListNode<T>(item, currentNode);
    192 
    193             // 4.3.把新节点当作上一节点的下一节点:如果索引是0,则新节点作为链接根节点
    194             if (index == 0) _rootNode = newNode;
    195             else prevtNode.Next = newNode;
    196 
    197             // 显示链表中的所有数据
    198             Console.Write($"AddAtBefore(int {index},T {item})	");
    199             Show();
    200         }
    201 
    202         #endregion
    203 
    204         #region 删除方法
    205 
    206 
    207         /// <summary>
    208         /// 5.删除: 删除T
    209         /// 5.1.得到 当前节点/上一节点/下一节点
    210         /// 5.2.把 上一节点的下一节点 更新为 下一节点
    211         /// </summary>
    212         /// <param name="item"></param>
    213         public void Remove(T item)
    214         {
    215             if (_rootNode == null) return;
    216             // 当前节点
    217             var currentNode = _rootNode;
    218             // 上一节点
    219             MyLinkedListNode<T> prevNode = null;
    220             while (currentNode.Next != null)
    221             {
    222                 if (currentNode.Node.Equals(item))
    223                 {
    224                     // 根据 当前节点 的 上一节点和下一节点 删除 当前节点
    225                     Remove(prevNode, currentNode.Next);
    226 
    227                     // 显示链表中的所有数据
    228                     Console.Write($"Remove({item})	");
    229                     Show();
    230                     return;
    231                 }
    232                 // 重置 上一节点 和 当前节点
    233                 prevNode = currentNode;
    234                 currentNode = currentNode.Next;
    235             }
    236             // 如果需要删除的是最后一个节点,则while循环在  currentNode == 倒数第二个节点时就会停止循环
    237             Remove(prevNode, null);
    238 
    239             // 显示链表中的所有数据
    240             Console.Write($"Remove({item})	");
    241             Show();
    242         }
    243 
    244         /// <summary>
    245         /// 根据 当前节点 的 上一节点和下一节点 删除 当前节点:把上一节点的next 指向 下一节点
    246         /// </summary>
    247         /// <param name="prevNode"></param>
    248         /// <param name="nextNode"></param>
    249         private void Remove(MyLinkedListNode<T> prevNode, MyLinkedListNode<T> nextNode)
    250         {
    251             if (prevNode == null) _rootNode = nextNode;
    252             else prevNode.Next = nextNode;
    253         }
    254 
    255         /// <summary>
    256         /// 6.删除: 删除指定索引
    257         /// 6.1.得到 当前/上一/下一节点
    258         /// 6.2.把当前节点的下一节点 更新为 下一节点
    259         /// </summary>
    260         /// <param name="index"></param>
    261         public void RemoveAt(int index)
    262         {
    263             var nodeTuple = GetNodeTupleAt(index);
    264 
    265             // 上一节点
    266             var prevNode = nodeTuple.Item2;
    267             // 判断上一节点是不是null ? 当前节点为根节点,需要把下一节点更新为更节点
    268             if (prevNode == null) _rootNode = nodeTuple.Item3;
    269             else prevNode.Next = nodeTuple.Item3;
    270 
    271 
    272             // 显示链表中的所有数据
    273             Console.Write($"RemoveAt({index})	");
    274             Show();
    275         }
    276 
    277         /// <summary>
    278         /// 7.删除: 删除第一个
    279         /// </summary>
    280         public void RemoveFirst()
    281         {
    282             if (_rootNode == null) return;
    283             _rootNode = _rootNode.Next;
    284 
    285             // 显示链表中的所有数据
    286             Console.Write($"RemoveFirst()	");
    287             Show();
    288         }
    289 
    290         /// <summary>
    291         /// 8.删除: 删除最后一个
    292         /// </summary>
    293         public void RemoveLast()
    294         {
    295             if (_rootNode == null) return;
    296             // 如果链表只存在根节点,则把根节点删除
    297             if (_rootNode.Next == null)
    298             {
    299                 _rootNode = null;
    300                 return;
    301             }
    302             // while循环获得最后一个节点
    303             var currentNode = _rootNode;
    304             // 上一节点/倒数第二个节点
    305             MyLinkedListNode<T> prevNode = null;
    306             while (currentNode.Next != null)
    307             {
    308                 prevNode = currentNode;
    309                 currentNode = currentNode.Next;
    310             }
    311             prevNode.Next = null;
    312 
    313             // 显示链表中的所有数据
    314             Console.Write($"RemoveLast()	");
    315             Show();
    316         }
    317 
    318         /// <summary>
    319         /// 9.删除: 删除所有
    320         /// </summary>
    321         public void Clear()
    322         {
    323             _rootNode = null;
    324 
    325             // 显示链表中的所有数据
    326             Console.Write($"Clear()	");
    327             Show();
    328         }
    329 
    330         #endregion
    331 
    332         /// <summary>
    333         /// 显示链表中的所有数据
    334         /// </summary>
    335         public void Show()
    336         {
    337             if (_rootNode == null)
    338             {
    339                 Console.WriteLine($"链表中的数据为空!
    ");
    340                 return;
    341             }
    342             StringBuilder builder = new StringBuilder();
    343 
    344             MyLinkedListNode<T> currentNode = _rootNode;
    345             while (currentNode.Next != null)
    346             {
    347                 builder.Append($"{currentNode.Node}	");
    348                 currentNode = currentNode.Next;
    349             }
    350             // 最后一个节点next为null,不会进入while
    351             builder.Append($"{currentNode.Node}	");
    352 
    353             Console.WriteLine($"链表中的数据为:
    {builder.ToString()}
    ");
    354         }
    355     }
    View Code

    测试代码:

     1         /// <summary>
     2         /// 测试单链表
     3         /// </summary>
     4         public static void RunLinkedList()
     5         {
     6             Console.WriteLine("======= 链表测试 Start =======");
     7 
     8             MyLinkedList<string> myLinkedList = new MyLinkedList<string>();
     9             myLinkedList.Append("张三");
    10             myLinkedList.Append("李四");
    11             myLinkedList.Append("王五");
    12             myLinkedList.Append("六麻子");
    13             myLinkedList.Append("田七");
    14             myLinkedList.Show(); // 张三    李四    王五    六麻子  田七
    15 
    16             // 异常测试
    17             var a = myLinkedList[1]; // 张三
    18             a = myLinkedList[3]; // 六麻子
    19             //a = myLinkedList[11];
    20 
    21             // 测试添加功能
    22             myLinkedList.AddFirst("郭大爷"); // 郭大爷  张三    李四    王五    六麻子  田七
    23             myLinkedList.AddAtAfter(0, "海大爷"); // 郭大爷  海大爷  张三    李四    王五    六麻子  田七
    24             myLinkedList.AddAtBefore(0, "Robot"); // Robot   郭大爷  海大爷  张三    李四    王五    六麻子  田七
    25             myLinkedList.AddAtBefore(2, "Robot"); // Robot   郭大爷  Robot   海大爷  张三    李四    王五    六麻子  田七
    26             myLinkedList.AddAtBefore(4, "Robot"); // Robot   郭大爷  Robot   海大爷  Robot   张三    李四    王五    六麻子  田七
    27 
    28             // 测试删除功能
    29             myLinkedList.Remove("Robot"); // 郭大爷  Robot   海大爷  Robot   张三    李四    王五    六麻子  田七
    30             myLinkedList.Remove("Robot"); // 郭大爷  海大爷  Robot   张三    李四    王五    六麻子  田七
    31             myLinkedList.Remove("田七"); // 郭大爷  海大爷  Robot   张三    李四    王五    六麻子
    32             myLinkedList.RemoveAt(0); // 海大爷  Robot   张三    李四    王五    六麻子
    33             myLinkedList.RemoveAt(1); // 海大爷  张三    李四    王五    六麻子
    34             myLinkedList.RemoveFirst(); // 张三    李四    王五    六麻子
    35             myLinkedList.RemoveFirst(); // 李四    王五    六麻子
    36             myLinkedList.RemoveLast(); // 李四    王五
    37             myLinkedList.RemoveLast(); // 李四
    38             myLinkedList.Clear(); // 链表中的数据为空!
    39 
    40             Console.WriteLine("======= 链表测试 End =======");
    41         }
  • 相关阅读:
    【西瓜书】周志华《机器学习》学习笔记与习题探讨(一)
    01-线性回归算法
    NumPy 字符串函数
    Numpy函数分类
    Excel一对多查询(index+small+if)
    支付机构MRC模
    数据分析方法论
    窗口函数/解析函数
    数据分析
    底层逻辑
  • 原文地址:https://www.cnblogs.com/Cailf/p/13186406.html
Copyright © 2011-2022 走看看