C# 单链表 | 双链表 | 循环链表的原理和代码实现

说明:C#有自己封装的双向循环链表LinkedList<>,足以满足日常工作需要。这里只是学习数据结构“单链表”的手搓代码,理解精神即可。

PS:这篇文章第一版写的有点烂,这是第二版,好看点。

PSS: 本文的重点代码代码代码!而不是数据结构的一些基础概念和分类;
完全没接触过的同学,可以去看B站王卓教授的视频。


1.什么是链表?

  • 用一组物理位置任意的存储单元来存放线性表的数据元素。
  • 存储单元可以是连续的,也可以是不连续的,甚至是零散分布在内存中
  • 链表中元素的逻辑次序和物理次序不一定相同。(即内存靠前的不一定逻辑次序靠前)

说明:

  1. 如图,链表第一个元素是0031,即“赵”,赵的下一个是0007,即“钱”,钱的下一个是0013,即“孙”,依此类推。
  2. 物理上这些地址连续吗?很明显,不连续
  3. 物理次序和逻辑次序有关系嘛?很明显,没关系。赵是第一个,物理地址却在其他元素的下面。

2.为什么使用链表?

  • 对比顺序表(例如数组),链表没有容量上限,可以无限扩容(不考虑内存的情况下)。而数组在初始化时便敲定了最大容量,无法扩容。
  • 已知某结点的情况下,删除、插入的时间复杂度为O(1),效率同比顺序表非常高。
  • 也就是说,对于某些情况下使用链表会带来更高的效率,那为什么不用呢?

3.链表有哪几种?

  • 单(向)链表
  • 双(向)链表
  • 循环链表

4.什么是“单向”链表?

  • 每个结点只有下一个结点的指针域,则由这类结点构成的链表叫单向链表。
  • 就是说: 你通过当前结点,只能向一个方向去找下一个结点,你找不到上一个,所以叫“单向”。

说明: 比如说你有a2的引用,你可以拿到a3,但你拿不到a1。


5.什么是“双向”链表?

  • 每个结点除了保留下一个结点的指针域外,还保留了上一个结点的指针域
  • 就是说:你通过当前结点,不仅可以拿到下一个结点,也能拿到上一个结点,你可以在前后两个方向获取元素,所以叫“双向”。

说明: 比如说你有a2的引用,你可以拿到a3,而且你也能拿到a1


6.什么是“循环”链表?

  • 循环链表的概念与“单、双”链表并非互斥关系,反而可以相互包容
  • 例如: 你可以实现 循环单链表 or 循环双链表
  • 那么,为什么叫“循环”呢? 因为,最后一个结点的Next指针一般是空的,因为的确没有结点了嘛,但是你的Next指针指向第一个元素,形成了一个环状结构。
    循环单链表
    循环双链表

7.单链表代码实现

namespace YoyoCode
{
    
    
    internal class SinglyLinkedList<T>
    {
    
    
        //首元结点
        public SinglyLinkedListNode<T> First;
        //尾结点
        public SinglyLinkedListNode<T> Last;
        //结点数量
        public int NodeCount = 0;

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <returns>bool,链表是否为空</returns>
        public bool IsEmpty()
        {
    
    
            return NodeCount == 0 ? true : false;
        }

        /// <summary>
        /// 清空链表
        /// </summary>
        public void Clear()
        {
    
    
            First = null;
            Last = null;
            NodeCount = 0;
        }

        /// <summary>
        /// 头插法
        /// </summary>
        /// <param name="value">类型为T的值</param>
        public void AddFirst(T value)
        {
    
    
            SinglyLinkedListNode<T> node = new SinglyLinkedListNode<T>(value);
            if (IsEmpty())
            {
    
    
                First = node;
                Last = node;
            }
            else
            {
    
    
                node.Next= First;
                First= node;
            }
            NodeCount++;
        }

        /// <summary>
        /// 尾插法
        /// </summary>
        /// <param name="value">类型为T的值</param>
        public void AddLast(T value)
        {
    
    
            SinglyLinkedListNode<T> node = new SinglyLinkedListNode<T>(value);
            if (IsEmpty())
            {
    
    
                First = node;
                Last = node;
            }
            else
            {
    
    
                Last.Next = node;
                Last = node;
            }
            NodeCount++;
        }
        /// <summary>
        /// 在对应的索引插入元素
        /// </summary>
        /// <param name="idx">索引,从0开始</param>
        public bool AddAt(T value, int idx)
        {
    
    
            if (idx < 0 || idx > NodeCount)
            {
    
    
                return false;
            }
            if (idx == 0)
            {
    
    
                AddFirst(value);
                return true;
            }
            if (idx == NodeCount)
            {
    
    
                AddLast(value);
                return true;
            }
            //其他情况
            SinglyLinkedListNode<T> prev = First;
            SinglyLinkedListNode<T> cur = First.Next;
            SinglyLinkedListNode<T> node = new SinglyLinkedListNode<T>(value);
            for (int i = 1; i < idx; i++)
            {
    
    
                prev = prev.Next;
                cur = cur.Next;
            }
            prev.Next = node;
            node.Next = cur;
            NodeCount ++;
            return true;
        }
        /// <summary>
        /// 删除第一个元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveFirst()
        {
    
    
            if (IsEmpty())
            {
    
    
                return false;
            }
            //链表只有一个元素
            if (NodeCount > 1 )
            {
    
    
                First = First.Next;
            }
            else
            {
    
    
                First = null;
                Last = null;
            }
            NodeCount--;
            return true;
        }

        /// <summary>
        /// 删除对应索引的元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveAt(int idx)
        {
    
    
            if (IsEmpty())
            {
    
    
                return false;
            }
            if (idx == 0)
            {
    
    
                return RemoveFirst();
            }

            SinglyLinkedListNode<T> prev = First;
            SinglyLinkedListNode<T> cur = First.Next;
            for (int i = 1; i < idx; i++)
            {
    
    
                prev = prev.Next;
                cur = cur.Next;
            }
            if (idx == NodeCount - 1)
            {
    
    
                Last = prev;
                prev.Next = null;
            }
            else
            {
    
    
                prev.Next = cur.Next;
            }
            NodeCount--;
            return true;
        }
        /// <summary>
        /// 删除最后一个元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveLast()
        {
    
    
            return RemoveAt(NodeCount - 1);
        }
    }

    /// <summary>
    /// 单向链表中的结点数据结构
    /// </summary>
    internal class SinglyLinkedListNode<T>
    {
    
    
        public T data;
        public SinglyLinkedListNode<T> Next;

        public SinglyLinkedListNode(T data)
        {
    
    
            this.data = data;
        }
    }
}

8.双链表代码实现

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace YoyoCode
{
    
    
    internal class DoubleLinkedList<T>
    {
    
    
        //首元结点
        public DoubleLinkedListNode<T> First;
        //尾结点
        public DoubleLinkedListNode<T> Last;
        //结点数量
        public int NodeCount = 0;

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <returns>bool,链表是否为空</returns>
        public bool IsEmpty()
        {
    
    
            return NodeCount == 0 ? true : false;
        }

        /// <summary>
        /// 清空链表
        /// </summary>
        public void Clear()
        {
    
    
            First = null;
            Last = null;
            NodeCount = 0;
        }

        /// <summary>
        /// 头插法
        /// </summary>
        /// <param name="value">类型为T的值</param>
        public void AddFirst(T value)
        {
    
    
            DoubleLinkedListNode<T> node = new DoubleLinkedListNode<T>(value);
            if (IsEmpty())
            {
    
    
                First = node;
                Last = node;
            }
            else
            {
    
    
                node.Next = First;
                First.Prev = node;
                First = node;
            }
            NodeCount++;
        }

        /// <summary>
        /// 尾插法
        /// </summary>
        /// <param name="value">类型为T的值</param>
        public void AddLast(T value)
        {
    
    
            DoubleLinkedListNode<T> node = new DoubleLinkedListNode<T>(value);
            if (IsEmpty())
            {
    
    
                First = node;
                Last = node;
            }
            else
            {
    
    
                Last.Next = node;
                node.Prev= Last;
                Last = node;
            }
            NodeCount++;
        }
        /// <summary>
        /// 在对应的索引插入元素
        /// </summary>
        /// <param name="idx">索引,从0开始</param>
        public bool AddAt(T value, int idx)
        {
    
    
            if (idx < 0 || idx > NodeCount)
            {
    
    
                return false;
            }
            if (idx == 0)
            {
    
    
                AddFirst(value);
                return true;
            }
            if (idx == NodeCount)
            {
    
    
                AddLast(value);
                return true;
            }
            //其他情况
            DoubleLinkedListNode<T> prev = First;
            DoubleLinkedListNode<T> cur = First.Next;
            DoubleLinkedListNode<T> node = new DoubleLinkedListNode<T>(value);
            for (int i = 1; i < idx; i++)
            {
    
    
                prev = prev.Next;
                cur = cur.Next;
            }
            prev.Next = node;
            node.Prev = prev;    
            node.Next = cur;
            cur.Prev= node;
            NodeCount++;
            return true;
        }
        /// <summary>
        /// 删除第一个元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveFirst()
        {
    
    
            if (IsEmpty())
            {
    
    
                return false;
            }
            //链表是否只有一个元素
            if (NodeCount > 1)
            {
    
    
                First.Next.Prev = null;
                First = First.Next;
            }
            else
            {
    
    
                First = null;
                Last = null;
            }
            NodeCount--;
            return true;
        }

        /// <summary>
        /// 删除对应索引的元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveAt(int idx)
        {
    
    
            if (IsEmpty())
            {
    
    
                return false;
            }
            if (idx == 0)
            {
    
    
                return RemoveFirst();
            }

            DoubleLinkedListNode<T> prev = First;
            DoubleLinkedListNode<T> cur = First.Next;
            for (int i = 1; i < idx; i++)
            {
    
    
                prev = prev.Next;
                cur = cur.Next;
            }
            if (idx == NodeCount - 1)
            {
    
    
                Last = prev;
                prev.Next = null;
            }
            else
            {
    
    
                prev.Next = cur.Next;
                prev.Next.Prev = prev;
            }
            NodeCount--;
            return true;
        }
        /// <summary>
        /// 删除最后一个元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveLast()
        {
    
    
            return RemoveAt(NodeCount - 1);
        }
    }

    /// <summary>
    /// 双向链表中的结点数据结构
    /// </summary>
    internal class DoubleLinkedListNode<T>
    {
    
    
        public T data;
        public DoubleLinkedListNode<T> Next;
        public DoubleLinkedListNode<T> Prev;

        public DoubleLinkedListNode(T data)
        {
    
    
            this.data = data;
        }
    }
}

说明

  1. 同比单向链表,多了一个Prev指针指向了上一个元素而已。
  2. 在处理删除、插入操作时,需要额外注意Prev指针的赋值即可。

9.循环单链表的代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace YoyoCode
{
    
    
    internal class CircularSLinkedList<T>
    {
    
    
        //尾结点
        public SinglyLinkedListNode<T> Last;
        //结点数量
        public int NodeCount = 0;

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <returns>bool,链表是否为空</returns>
        public bool IsEmpty()
        {
    
    
            return NodeCount == 0 ? true : false;
        }

        /// <summary>
        /// 清空链表
        /// </summary>
        public void Clear()
        {
    
    
            Last = null;
            NodeCount = 0;
        }

        /// <summary>
        /// 头插法
        /// </summary>
        /// <param name="value">类型为T的值</param>
        public void AddFirst(T value)
        {
    
    
            SinglyLinkedListNode<T> node = new SinglyLinkedListNode<T>(value);
            if (IsEmpty())
            {
    
    
                Last = node;
                Last.Next = Last;
            }
            else
            {
    
    
                node.Next = Last.Next;
                Last.Next = node;
            }
            NodeCount++;
        }

        /// <summary>
        /// 尾插法
        /// </summary>
        /// <param name="value">类型为T的值</param>
        public void AddLast(T value)
        {
    
    
            SinglyLinkedListNode<T> node = new SinglyLinkedListNode<T>(value);
            if (IsEmpty())
            {
    
    
                Last = node;
                Last.Next = Last;
            }
            else
            {
    
    
                Last.Next = node;
                Last = node;
            }
            NodeCount++;
        }
        /// <summary>
        /// 在对应的索引插入元素
        /// </summary>
        /// <param name="idx">索引,从0开始</param>
        public bool AddAt(T value, int idx)
        {
    
    
            if (idx < 0 || idx > NodeCount)
            {
    
    
                return false;
            }
            if (idx == 0)
            {
    
    
                AddFirst(value);
                return true;
            }
            if (idx == NodeCount)
            {
    
    
                AddLast(value);
                return true;
            }
            //其他情况
            SinglyLinkedListNode<T> prev = Last.Next;
            SinglyLinkedListNode<T> cur = Last.Next.Next;
            SinglyLinkedListNode<T> node = new SinglyLinkedListNode<T>(value);
            for (int i = 1; i < idx; i++)
            {
    
    
                prev = prev.Next;
                cur = cur.Next;
            }
            prev.Next = node;
            node.Next = cur;
            NodeCount++;
            return true;
        }
        /// <summary>
        /// 删除第一个元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveFirst()
        {
    
    
            if (IsEmpty())
            {
    
    
                return false;
            }
            //链表只有一个元素
            if (NodeCount > 1)
            {
    
    
                Last.Next = Last.Next.Next;
            }
            else
            {
    
    
                Last = null;
            }
            NodeCount--;
            return true;
        }

        /// <summary>
        /// 删除对应索引的元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveAt(int idx)
        {
    
    
            if (IsEmpty())
            {
    
    
                return false;
            }
            if (idx == 0)
            {
    
    
                return RemoveFirst();
            }

            SinglyLinkedListNode<T> prev = Last.Next; 
            SinglyLinkedListNode<T> cur = Last.Next.Next;
            for (int i = 1; i < idx; i++)
            {
    
    
                prev = prev.Next;
                cur = cur.Next;
            }
            if (idx == NodeCount - 1)
            {
    
    
                prev.Next = Last.Next;
                Last = prev;
            }
            else
            {
    
    
                prev.Next = cur.Next;
            }
            NodeCount--;
            return true;
        }
        /// <summary>
        /// 删除最后一个元素
        /// </summary>
        /// <returns>bool,是否删除成功</returns>
        public bool RemoveLast()
        {
    
    
            return RemoveAt(NodeCount - 1);
        }
    }
}

说明

  1. 同比单向链表,不需要First指针了
  2. 在处理头尾的删除、插入操作时,需要额外注意Last指针Next指针的赋值即可。
  3. Last.Next实际上就是First,因为可以通过Last直接找到First,所以删除掉了。即尾指针的Next指向了链表的第一个元素,形成了一个环。


结语
诶?是不是想问为啥我突然弄数据结构了?
诶!面试两家都在数据结构和算法这儿碰壁了,淦。。。
咱就是说,工作了两年半(bushi),基本没用到过什么堆排、快排、二叉树什么的,但人家就是问了。。。
我也很绝望啊!于谦:那怎么办?当然是肝了哈哈哈哈,线性表两天肝完了顺序表+单链表的原理和相应增删改查算法。
加油!!!

猜你喜欢

转载自blog.csdn.net/Liyager/article/details/128886734