[Data Structure] Single Linked List of Linked List

Table of contents

Basic concepts and operations:

Implementation method and principle

Application Scenarios and Precautions for Use

Algorithm and complexity analysis:

Comparison with other data structures:


Basic concepts and operations:

A singly linked list is a common linear data structure consisting of several nodes, and each node contains two parts: a data element and a pointer to the next node. Each node has only one pointer, usually called the next pointer, which points to the node's successor node. The head node is an additional node added before the first node, which does not contain data elements, but contains a pointer to the first node.

Singly linked list can represent a sequence of any length, and can dynamically insert and delete nodes, making it flexible and efficient. But since each node has only one pointer, the singly linked list can only be traversed from front to back, not reverse.

The nodes of the singly linked list are described in C# language as:

namespace DataStructLibrary
{
    class SNode<T>
    {
        private T data;//数据域
        private SNode<T> next;//引用域
        public SNode(T val,SNode<T> p)
        {
            data = val;
            next = p;
        }

        public SNode(SNode<T> p)
        {
            next = p;

        }

        public SNode(T val)
        {
            data = val;
            next = null;
        }

        public SNode()
        {
            data = default(T);
            next = null;
        }

        public T Data
        {
            get { return data; }
            set { data = value; }
        }

        public SNode<T> Next
        {
            get { return next; }
            set { next = value; }
        }
    }
}

The following are commonly used operations on singly linked lists:

  1. Create linked list: Create an empty linked list, with or without a head node.

step operate
1 Declare a start variable of node type to point to the first node of the singly linked list
2

Assign the value of the start variable to null in the constructor of the singly linked list

     2. Insert node: Insert a new node at the specified position (such as the head, tail or middle) of the linked list.

         2.1 Insert a new node at the beginning of the singly linked list

        2.2 Insert a node between two nodes in the linked list

        2.3 Insert a new node at the end of the linked list

Inserting a node at the end of the singly linked list is a special case of inserting a node between two nodes of the linked list. When current is null and previous points to the last node, the new node can be inserted at the end of the linked list. If in some cases, it is very clear that the node is to be inserted at the end of the linked list, the following algorithm steps can be performed.

step operate
1

Allocate memory for new nodes and assign values ​​to data fields

2

Find the last node in the list, mark it as current

3

Point the next field of current to the new node

4

It is the next field of the knot pointing to null, releasing the current space

      3. Delete operation

      To delete a specified node from a singly linked list, first determine whether the list is empty. If it is not empty, the specified node must be searched first, and if the specified node is found, it will be deleted, otherwise it will give a prompt message that the corresponding node is not found. When the deleted node is found, the specified node is deleted in the singly linked list, usually divided into the following three situations:

         3.1 Delete the head node of the singly linked list

step operate
1

mark the first node in the list as the current node

2

Use start to point to the next node in the singly linked list

3

free memory marked as current node

         3.2 Delete the node between two nodes in the singly linked list

         3.3 Delete the tail node of the singly linked list

         In the above algorithm for deleting nodes between two nodes in the one-way linked list, if after the search operation, the current node current points to the last node in the list, it means that the node to be deleted is the last node in the list. The algorithm can also delete nodes at the end of unidirectional chain expressions. So there is no need to specifically create an algorithm for removing nodes at the end of a singly linked list

     4. Take table elements and locate elements

     Fetching table elements and locating elements refers to searching for the node corresponding to the sequence number or value according to the given sequence number or node value. The specific process is as follows:

 step

operate

1

Mark the starting node of the singly linked list as the current node current

2

If the single linked list is not an empty linked list, compare whether the serial number or value to be searched is equal to the serial number or value pointed to by the current reference, if not, current points to the next node, and when the node is found, return current

3

When current is null, it means that the specified node was not found

      5. Reverse linked list: Arrange the nodes in the linked list in reverse order.

      6. Append linked list: append the head of another linked list to this linked list.

The above operations are the most basic operations of a singly linked list, through which many other data structures such as stacks, queues, and hash tables can be implemented. It should be noted that some boundary conditions and abnormal conditions need to be dealt with when performing linked list operations to ensure the normal operation of the linked list.

Other operations related to the linear table, such as calculating the length of the table, judging that it is empty, etc., are relatively simple to implement in the sequential table, see the following C# code for the single-linked list:

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

namespace DataStructLibrary
{
    class SingleLinkList<T>:ILinarList<T>
    {
        public SNode<T> start;//单链表的头引用
        int length;//单链表长度
        /// <summary>
        /// 初始化单链表
        /// </summary>
        public SingleLinkList()
        {
            start = null;
        }


        /// <summary>
        /// 在单链表的末尾追加数据元素 data
        /// </summary>
        /// <param name="data"></param>
        public void InsertNode(T data)
        {
            if(start == null)
            {
                start = new SNode<T>(data);
                length++;
                return;
            }
            SNode<T> current = start;
            while(current.Next != null)
            {
                current = current.Next;
            }
            current.Next = new SNode<T>(data);
            length++;
        }

        /// <summary>
        /// 在单链表的第i个数据元素的位置前插入一个数据元素data
        /// </summary>
        /// <param name="data"></param>
        /// <param name="i"></param>
        public void InsertNode(T data, int i)
        {
            SNode<T> current;
            SNode<T> previous;
            if(i<1 || i> (length + 1))
            {
                Console.WriteLine("Position is error!");
                return;
            }
            SNode<T> newNode = new SNode<T>(data);
            //在空链表或第一个元素钱插入第一个元素
            if (i == 1)
            {
                newNode.Next = start;
                start = newNode;
                length++;
                return;
            }
            //单链表的两个元素中间插入一个元素
            current = start;
            previous = null;
            int j = 1;
            while(current != null && j<i)
            {
                previous = current;
                current = current.Next;
                j++;
            }
            if (j == i)
            {
                previous.Next = newNode;
                newNode.Next = current;
                length++;
            }
        }

        /// <summary>
        /// 删除单链表的第i个数据元素
        /// </summary>
        /// <param name="i"></param>
        public void DeleteNode(int i)
        {
            if(IsEmpty() || i < 1)
            {
                Console.WriteLine("Link is empty or Position is error");
            }
            SNode<T> current = start;
            if(i == 1)
            {
                start = current.Next;
                length--;
                return;
            }
            SNode<T> previus = null;
            int j = 1;
            while(current.Next != null && j<i)
            {
                previus = current;
                current = current.Next;
                j++;
            }
            if (j == i)
            {
                previus.Next = current.Next;
                current = current.Next;
                length--;
                return;
            }
            //第i个节点不存在
            Console.WriteLine("the ith node is not exist!");

        }

        /// <summary>
        /// 获取单链表的第i个数据元素
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public T SearchNode(int i)
        {
            if (IsEmpty())
            {
                Console.WriteLine("List is empty");
                return default(T);
            }
            SNode<T> current = start;
            int j = 1;
            while (current.Next != null && j<i)
            {
                current = current.Next;
                j++;
            }
            if (j == i)
            {
                return current.Data;
            }
            //第i个节点不存在
            Console.WriteLine("the ith node is not exist!");
            return default(T);

        }

        /// <summary>
        /// 在单链表中查找值为data的数据元素
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public T SearchNode(T data)
        {
            if (IsEmpty())
            {
                Console.WriteLine("List is empty");
                return default(T);
            }
            SNode<T> current = start;
            int i = 1;
            while(current != null && !current.Data.ToString().Contains(data.ToString()))
            {
                current = current.Next;
                i++;
            }
            if(current != null)
            {
                return current.Data;
            }
            return default(T);
        }

        /// <summary>
        /// 获取单链表的长度
        /// </summary>
        /// <returns></returns>
        public int GetLength()
        {
            return length;
        }

        /// <summary>
        /// 该函数将链表头节点反转后,重新作为链表的头节点。算法使用迭代方式实现,遍历链表并改变指针指向
        /// 例如链表头结点start:由原来的
        /// data:a,
        /// next:[
        ///      data:b,
        ///      next:[
        ///           data:c,
        ///           next:null
        ///          ]
        ///     ] 
        ///翻转后的结果为:
        /// data:c,
        /// next:[
        ///      data:b,
        ///      next:[
        ///           data:a,
        ///           next:null
        ///          ]
        ///     ] 
        /// </summary>
        public void ReverseList()
        {
            if (length ==1 || this.start == null)
            {
                return;
            }
            //定义 previous next 两个指针
            SNode<T> previous = null;
            SNode<T> next = null;
            SNode<T> current = this.start;
            //循环操作
            while (current != null)
            {
                //定义next为Head后面的数,定义previous为Head前面的数
                next = current.Next;
                current.Next = previous;//这一部分可以理解为previous是Head前面的那个数。
                //然后再把previous和Head都提前一位
                previous = current;
                current = next;
            }
            this.start = previous;
            //循环结束后,返回新的表头,即原来表头的最后一个数。
            return;
        }



        /// <summary>
        /// 将另外一个链表头部追加到本链表
        ///  /// 例如链表头结点start:由原来的
        /// data:a,
        /// next:[
        ///      data:b,
        ///      next:[
        ///           data:c,
        ///           next:null
        ///          ]
        ///     ] 
        ///翻转后的结果为:
        /// data:a,
        /// next:[
        ///      data:b,
        ///      next:[
        ///           data:c,
        ///           next:[
        ///                 data:x,
        ///                 next:[
        ///                       data:y,
        ///                       next:[
        ///                             data:z,
        ///                             next:null
        ///                            ]
        ///              
        ///                      ]
        ///               ]
        ///          ]
        ///     ] 
        /// </summary>
        /// <param name="nodeB">
        /// 追加的单链表头部数据
        /// 例如:
        /// data:x,
        /// next:[
        ///      data:y,
        ///      next:[
        ///           data:z,
        ///           next:null
        ///          ]
        ///     ]
        /// </param>
        public void Append(SNode<T> nodeB)
        {
            if (nodeB == null)
            {
                return;
            }
            InsertNode(nodeB.Data);
            SNode<T> tmpNode = nodeB.Next;
            while (tmpNode != null)
            {
                InsertNode(tmpNode.Data);
                tmpNode = tmpNode.Next;
            }

            return;
        }

        /// <summary>
        /// 清空单链表
        /// </summary>
        public void Clear()
        {
            start = null;
        }

        /// <summary>
        /// 判断单链表是否为空
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty()
        {
            if(start == null)
            {
                return true;
            }
            return false;
        }

    }
}

Implementation method and principle

The implementation method and principle of singly linked list mainly include the following aspects:

  1. Node: A node in a singly linked list consists of two parts, namely the data field and the pointer field. The data field stores the data elements of the node, and the pointer field points to the next node. Nodes can be represented by structures.

  2. Head node: The head node is an additional node added in front of the first node in the linked list. It does not contain data elements, but contains a pointer to the first node. The role of the head node is to simplify the operation of the linked list, making operations such as insertion and deletion more convenient.

  3. Pointer operation: The connection between nodes in the linked list is realized through pointers. Each node has a pointer pointing to the next node, and the pointer relationship between nodes needs to be modified when inserting or deleting nodes.

  4. Traversing a linked list: Traversing a linked list refers to visiting all nodes in the linked list in order. By traversing the linked list, you can read or modify the data value in the node, and you can also calculate the length of the linked list and find a certain node.

  5. Memory management: memory space needs to be allocated or released when inserting and deleting nodes. In order to avoid problems such as memory leaks and repeated releases, it is necessary to manage the memory space reasonably.

  6. Boundary condition processing: Some boundary conditions and abnormal conditions need to be dealt with when performing linked list operations, such as the linked list is empty, the insertion position is out of range, etc., to ensure the normal operation of the linked list.

Understanding the implementation method and principle of singly linked list is very helpful for understanding the performance and optimization of linked list. At the same time, it is necessary to pay attention to reasonable design and optimization according to the actual situation in the specific implementation to improve the efficiency and maintainability of the code.

Application Scenarios and Precautions for Use

Singly linked list is a common data structure, often used in the following application scenarios:

  1. Implement stacks and queues: Singly linked lists can be used to implement stacks and queues. In a stack, elements can only enter and exit from the top of the stack; in a queue, elements can only enter from the end of the queue and exit from the head of the queue. These two data structures can be easily implemented by using the head insertion and tail insertion operations of the singly linked list.

  2. Memory allocation: In computer memory management, singly linked lists are often used as data structures for dynamic memory allocation. Through the pointer connection between the nodes of the linked list, memory blocks can be dynamically allocated and released.

  3. Audio and Video Playlists: Singly linked lists can be used to implement audio and video playlists. Each node represents an audio or video file and holds the position of the next file. By traversing the linked list, the audio and video in the entire list can be played sequentially.

  4. Finding circular structures: Singly linked lists can also be used to deal with problems related to circular structures, such as judging whether a linked list has a ring, finding the entry of the ring, etc.

  5. Cache elimination strategy: In the cache system, when the cache space is full, some data needs to be eliminated to make room. Singly linked lists can be used to maintain data items in the cache while recording their usage. When data needs to be eliminated, the least recently used data item can be selected for elimination, that is, the node at the end of the singly linked list is deleted.

The following points need to be paid attention to when using singly linked list:

  1. Null pointer problem: Null pointer problems are prone to occur in linked list operations, such as accessing an empty linked list or a node that does not exist. In order to avoid these problems, it is necessary to perform null judgment on the input parameters.

  2. Memory management issues: When inserting and deleting nodes, memory space needs to be allocated or released. If the management is not done properly, problems such as memory leaks or repeated releases may occur. These problems can be solved by using garbage collection mechanism or manually managing memory space.

  3. Boundary condition problem: Some boundary conditions and abnormal conditions need to be dealt with when performing linked list operations, such as the linked list is empty, the insertion position is out of range, etc., to ensure the normal operation of the linked list.

  4. Performance issues: When dealing with large-scale data, singly linked lists may have some performance issues, such as slow random access speed and large space overhead. Therefore, in practical applications, it is necessary to select an appropriate data structure according to the actual situation.

Algorithm and complexity analysis:

  1. Understand the algorithms and complexity analysis of singly linked lists, such as how to judge whether a linked list has a ring, how to reverse a linked list, how to merge two ordered linked lists, etc. Mastering these algorithms can improve your programming skills while deepening your understanding of singly linked lists.

Singly linked list algorithm and complexity analysis mainly include the following aspects:

  1. Traversing a linked list: Traversing a linked list is the basic operation of accessing all nodes in the linked list, which can be implemented through loops or recursion. The time complexity is O(n), where n is the length of the linked list.

  2. Find the specified node: Search for the specified node in the linked list, you can search according to the position of the node or the keyword of the node. The time complexity of linear search is O(n), and the time complexity of binary search is O(logn).

  3. Insert node: To insert a new node in the linked list, the pointer relationship between nodes needs to be modified. Time complexity is O(1) if inserting at the beginning or end, otherwise O(n).

  4. Delete node: To delete a node in the linked list, you need to modify the pointer relationship between nodes. If the deletion is the head node or tail node, the time complexity is O(1), otherwise it is O(n).

  5. Reverse linked list: To arrange the nodes in the linked list in reverse order, the pointer relationship between nodes needs to be modified. It can be implemented using iteration or recursion, and the time complexity is O(n).

  6. Merge linked list: To merge two ordered linked lists into a new ordered linked list, you need to compare the nodes of the two linked lists one by one and merge them. The time complexity is O(m+n), where m and n represent the lengths of the two linked lists respectively.

It should be noted that when performing the linked list algorithm, it is necessary to pay attention to dealing with some boundary conditions and abnormal conditions, such as the linked list is empty, the insertion position is out of range, and so on. At the same time, in the actual implementation, reasonable design and optimization should be carried out according to the actual situation to improve the efficiency and maintainability of the code.

Comparison with other data structures:

  1. Learn how singly linked lists compare to other data structures such as arrays, doubly linked lists, etc. Understanding their advantages and disadvantages and applicable scenarios can better select the appropriate data structure to solve practical problems.

The comparison between singly linked list and other data structures mainly includes the following aspects:

  1. Arrays: Both arrays and singly linked lists can be used to represent sequences, but they are implemented differently. An array is a continuous allocation of storage space in memory, and any element can be directly accessed. The singly linked list needs to connect nodes through pointers, and can only be traversed from front to back. Since each node of the singly linked list has only one pointer, the space overhead is small; while the array needs to pre-allocate a certain size of memory space, and some space may be wasted when inserting or deleting elements.

  2. Stacks and queues: Stacks and queues are two common data structures that can be implemented using arrays or singly linked lists. Stacks and queues implemented with arrays can be accessed randomly, but other elements need to be moved when inserting or deleting elements, and the time complexity is O(n). The stack and queue implemented by singly linked list can perform insertion and deletion operations at the head or tail, and the time complexity is O(1).

  3. Hash table: A hash table is a data structure based on a hash function, which can quickly find and modify elements. In the implementation of hash tables, arrays are usually used to store elements, and linked lists or other data structures are used to handle hash collisions. Singly linked list can be used as an implementation of chained storage in hash table, but since it can only be traversed from front to back, it may affect the query efficiency of hash table.

  4. Red-black tree: Red-black tree is a self-balancing binary search tree, which can realize fast insertion, deletion and search operations. Compared with data structures such as singly linked lists, red-black trees have lower time complexity and support advanced functions such as range search and ranking operations. But correspondingly, it is also more complicated to implement, and needs to deal with various rotation and coloring operations.

To sum up, the singly linked list is suitable for representing sequences, and supports dynamic insertion and deletion operations with less space overhead. Compared with arrays, the time complexity of inserting and deleting elements is lower; compared with other data structures such as stacks and queues, it is more convenient to perform insertion and deletion operations in the middle. However, it has poor performance in operations such as query and sorting, and is not as good as advanced data structures such as red-black trees.

PS: If there are any mistakes or omissions, please correct me

Guess you like

Origin blog.csdn.net/beenles/article/details/131110584