C# single linked list | double linked list | principle and code implementation of circular linked list

Explanation : C# has its own encapsulated two-way circular linked list LinkedList<>, which is enough to meet the needs of daily work. This is just to learn the hand rubbing code of the data structure "single linked list", and understand the spirit.

PS: The first edition of this article was a bit rotten, this is the second edition, better.

PSS: The focus of this article is code code code ! Rather than some basic concepts and classifications of data structures;
students who have never been in contact with it can watch the video of Professor Wang Zhuo at station B.


1. What is a linked list?

  • Use a group of storage units with arbitrary physical locations to store the data elements of the linear table.
  • Storage units can be continuous or discontinuous , or even scattered in memory ;
  • The logical order and physical order of the elements in the linked list are not necessarily the same . (That is, the front of the memory is not necessarily the front of the logical order)

illustrate:

  1. As shown in the figure, the first element of the linked list is 0031, which is "Zhao", the next element of Zhao is 0007, which is "Qian", the next element of Qian is 0013, which is "Sun", and so on.
  2. Are these addresses physically contiguous? Obviously, discontinuous .
  3. Is physical order related to logical order? Obviously, it's okay . Zhao is the first, but the physical address is below the other elements.

2. Why use a linked list?

  • Compared with sequential tables (such as arrays), linked lists have no upper limit on capacity and can be expanded infinitely (regardless of memory). However, the maximum capacity of the array is determined when it is initialized and cannot be expanded.
  • When a certain node is known, the time complexity of deletion and insertion is O(1) , and the efficiency is very high compared to the sequence table.
  • In other words, for some cases , using a linked list will bring higher efficiency , so why not use it?

3. What are the linked lists?

  • Single (direction) linked list
  • Doubly linked list
  • circular linked list

4. What is a "one-way" linked list?

  • Each node has only the pointer field of the next node , and a linked list composed of such nodes is called a one-way linked list.
  • That is to say: you can only find the next node in one direction through the current node, and you can't find the previous one , so it is called "one-way".

Explanation: Say you have a reference to a2, you can get a3, but you can't get a1.


5. What is a "two-way" linked list?

  • In addition to retaining the pointer field of the next node, each node also retains the pointer field of the previous node .
  • That is to say: through the current node, you can not only get the next node, but also get the previous node . You can get elements in both directions , so it is called "two-way".

Explanation: Say you have a reference to a2, you can get a3, and you can also get a1 .


6. What is a "circular" linked list?

  • The concept of circular linked list and "single and double" linked list are not mutually exclusive , but can be mutually inclusive ;
  • For example: you can implement circular singly linked list or circular double linked list .
  • So, why is it called "cycle"? Because, the Next pointer of the last node is generally empty, because there is indeed no node, but your Next pointer points to the first element , forming a ring structure.
    circular singly linked list
    circular double linked list

7. Singly linked list code implementation

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. Double linked list code implementation

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;
        }
    }
}

Description :

  1. Compared with the one-way linked list, there is one more Prev pointer pointing to the previous element.
  2. When dealing with delete and insert operations, you need to pay extra attention to the assignment of the Prev pointer.

9. Code for circular singly linked list

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);
        }
    }
}

Description :

  1. Compared with the one-way linked list, the First pointer is not needed .
  2. When dealing with deletion and insertion operations at the head and tail , you need to pay extra attention to the assignment of the Next pointer in the Last pointer .
  3. Last.Next is actually First , because First can be found directly through Last, so it is deleted. That is, the Next of the tail pointer points to the first element of the linked list, forming a ring.


Conclusion :
Huh? Do you want to ask why I suddenly made a data structure?
Eh! Both of the interviews hit a wall in the data structure and algorithm, Gan. . .
Let's just say that after working for two and a half years (bushi), I have basically never used heaps, quick sorts, binary trees, etc., but people just asked. . .
I am also very desperate! Yu Qian: So what should we do? Of course it’s the liver hahahaha, the principle of the sequence table + single linked list and the corresponding addition, deletion, modification and query algorithm have been completed in two days.
come on! ! !

Guess you like

Origin blog.csdn.net/Liyager/article/details/128886734