[Data Structure] Circular 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:

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


Basic concepts and operations:

A circular linked list is a special linked list structure, the last node of which points to the first node, forming a loop, so that triggering from any node in the list can find other nodes in the list. Circular linked list can be divided into two types: one-way circular linked list and two-way circular linked list. The following is a schematic diagram of a circular singly linked list with a leading node.

Schematic diagram of a circular singly linked list with a head pointer

For a circular linked list, sometimes changing the pointer to a tail pointer will make the operation easier. The following figure is a schematic diagram of a circular single linked list with a tail pointer.

Schematic diagram of circular singly linked list with tail pointer

As can be seen from the schematic diagram above, the logical relationship of using a circular table to represent a linear table is the same as that of a singly linked list. The difference is that the next value of the last element cannot be null, but the first one in the stored linked list. The address of the element.

The following are common operations on circular linked lists:

  1. Head node: The node before the first node of the circular linked list, usually does not store data, and its main function is to facilitate the operation of the circular linked list.

  2. Tail node: The last node of the circular linked list, whose next pointer points to the head node.

  3. Insert element: To insert a new element at a specified position, you need to modify the next pointer of the previous node and the next pointer of the new node.

  4. Delete element: To delete the element at the specified position, you need to modify the next pointer of the previous node.

  5. Find elements: traverse the circular linked list from the head node until the target element is found or the entire linked list is traversed.

  6. Traversing elements: Traversing all elements of the circular linked list from the head node can be implemented using a while loop or a for loop.

  7. Reversing the linked list: Reversing the order of all nodes in the circular linked list requires the use of three pointers.

It should be noted that when performing various operations, it is necessary to ensure the continuity and order of the circular linked list, that is, the next pointer of each node points to the next node, and the next pointer of the last node points to the head node. See the C# code below for implementation details:

  /// <summary>
    /// 循环单链表数据结构实现接口具体步骤
    /// </summary>
    /// <typeparam name="T"></typeparam>
     class CLinkedList<T>:ILinarList<T>
    {
        public SNode<T> tail;
        int length;//循环链表长度

        public CLinkedList()
        {
            this.tail = null;
        }


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

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


        /// <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 = tail;
            SNode<T> previus = null;
            if (i == 1)
            {
                tail.Data = current.Next.Data;
                tail.Next = current.Next.Next;
                length--;
                return;
            }
            if (i > length)
            {
                return;
            }

            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 = tail;
            int j = 1;
            while (current.Next != null && j < i && j<=length)
            {
                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 = tail;
            int i = 1;
            bool isFound = false;
            while (current != null && i<=length)
            {
                if (current.Data.ToString().Contains(data.ToString()))
                {
                    isFound = true;
                    break;
                }
                current = current.Next;
                i++;
            }
            if (isFound)
            {
                return current.Data;
            }
            return default(T);
        }

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


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



        public bool IsEmpty()
        {
            return length == 0;
        }

        /// <summary>
        /// 该函数将链表头节点反转后,重新作为链表的头节点。算法使用迭代方式实现,遍历链表并改变指针指向
        /// 例如链表头结点start:由原来的
        /// data:a,
        /// next:[
        ///      data:b,
        ///      next:[
        ///           data:c,
        ///           next:[
        ///                data:a,
        ///                next:[
        ///                     data:b,
        ///                     next:...
        ///                     ]
        ///                ]
        ///           ]
        ///      ] 
        ///翻转后的结果为:
        /// data:c,
        /// next:[
        ///      data:b,
        ///      next:[
        ///           data:a,
        ///           next:[
        ///                 data:c,
        ///                 next:[
        ///                       data:b,
        ///                       next:....
        ///                      ]
        ///                ]
        ///          ]
        ///     ] 
        /// </summary>
        // 反转循环链表
        public void ReverseList()
        {

            if (length == 1 || this.tail == null)
            {
                return;
            }
            //定义 previous next 两个指针
            SNode<T> previous = null;
            SNode<T> next;
            SNode<T> current = this.tail;
            //循环操作
            while (current != null)
            {
                //定义next为Head后面的数,定义previous为Head前面的数
                next = current.Next;
                current.Next = previous;//这一部分可以理解为previous是Head前面的那个数。
                //然后再把previous和Head都提前一位
                previous = current;
                current = next;
            }
            this.tail = previous.Next;
            //循环结束后,返回新的表头,即原来表头的最后一个数。
            return;

        }
    }

In short, the circular linked list is a special linked list structure, which is widely used in practical applications. It should be noted that when performing various operations, it is necessary to ensure the continuity and order of the circular linked list to avoid problems such as data inconsistency.

Implementation method and principle:

A circular linked list is a special one-way or two-way linked list structure, the next pointer of the last node points to the first node, forming a loop. I have introduced the implementation methods and principles of one-way and two-way circular linked lists in other articles.

In short, the circular linked list is a special linked list structure, and care must be taken to ensure the continuity and order of the linked list during implementation.

Application scenarios and precautions for use:

A circular linked list is a special linked list structure, which is suitable for scenarios that need to iterate through nodes. The following are some common circular linked list application scenarios:

  1. Joseph problem: The Joseph problem is a classic mathematical problem that can be implemented with a circular linked list. Specifically, a circular linked list can be used to simulate a group of people forming a circle, and then kill each Mth person in turn, and the last remaining person is the winner.

  2. Cache elimination algorithm: In the cache elimination algorithm, a circular linked list can be used to implement the LRU (Least Recently Used) algorithm. Specifically, the data in the cache can be formed into a circular linked list in order of access time, and when the cache is full, delete the data that has not been accessed for the longest time.

  3. Marquee effect: In the development of graphical interface, a circular linked list can be used to achieve the marquee effect, even if a piece of text is displayed in a circular scrolling manner.

When using a circular linked list, you need to pay attention to the following points:

  1. The operation of a circular linked list is similar to that of a normal linked list, and it is relatively easy to insert, delete, and search. However, it is necessary to pay attention to the continuity and order of the circular linked list to avoid problems such as infinite loops or data inconsistencies.

  2. The nodes of the circular linked list need to store an additional pointer, so it takes up more space than the ordinary linked list.

  3. When using a two-way circular linked list, it is necessary to pay attention to the processing of the head node and the tail node, so as to avoid the breakage of the linked list due to operation errors.

In short, a circular linked list is a special linked list structure, suitable for scenarios that require iterative access to nodes. In practical applications, it is necessary to select an appropriate data structure according to specific needs and scenarios, and pay attention to its characteristics and usage precautions.

Algorithm and complexity analysis:

Some basic algorithms for circular linked lists include:

  1. Insert an element at a specified position: You need to find the previous node at the insertion position first, then point the next pointer of the new node to the node at the insertion position, and then point the next pointer of the previous node to the new node. The time complexity is O(n).

  2. Delete an element at a specified position: You need to find the previous node at the deletion position, and then point its next pointer to the next node at the deletion position. The time complexity is O(n).

  3. Finding elements: It is necessary to traverse the circular linked list from the head node until the target element is found or the entire linked list is traversed. The time complexity is O(n).

  4. Traversing elements: Traversing all elements of the circular linked list from the head node can be implemented using a while loop or a for loop. The time complexity is O(n).

  5. Reversing the linked list: Reversing the order of all nodes in the circular linked list requires the use of three pointers. The time complexity is O(n).

It should be noted that in the circular linked list, the insertion and deletion operations need to additionally consider the modification of the next pointer of the last node and the prev pointer of the first node. At the same time, due to the special nature of the circular linked list, when traversing and searching for elements, it is necessary to determine whether the condition for the end of the loop has been met.

In short, the circular linked list is a special linked list structure. When implementing various operations, it is necessary to pay attention to its continuity and order, and to deal with special situations accordingly. In practical applications, it is necessary to select appropriate algorithms and data structures according to specific needs and scenarios, and make trade-offs and compromises.

Comparison with other data structures:

Circular linked list is a special linked list structure, which has the following advantages and disadvantages compared with other data structures:

  1. Compared with ordinary linked lists, circular linked lists are more flexible in operation, and can realize functions such as iterative access and traversal. However, in operations such as insertion, deletion, and lookup, its time complexity is the same as that of a normal linked list.

  2. Compared with arrays, circular linked lists can be dynamically expanded and support efficient insertion and deletion operations. However, when accessing elements randomly, its time complexity is high, and it is not as efficient as an array.

  3. Compared with stacks and queues, circular linked lists can store more elements and support more flexible access methods. However, when inserting and deleting elements, its time complexity is relatively high, and it is not suitable for frequent operations of this kind.

  4. Compared with complex data structures such as trees and graphs, circular linked lists are simple to implement, easy to understand and maintain. However, when searching and traversing a large amount of data, its time complexity is high, which is not suitable for such application scenarios.

  5. Compared with the hash table, the lookup efficiency of the circular linked list is low, and it does not support fast insertion and deletion operations. However, its implementation is simple and there is no need to deal with issues such as hash collisions.

In short, circular linked list is a special linked list structure, which needs to be selected according to specific scenarios and requirements in practical applications. It should be noted that there are trade-offs and compromises between different data structures. When using them, their advantages and disadvantages need to be considered comprehensively, and appropriate data structures and algorithms should be selected.

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

Guess you like

Origin blog.csdn.net/beenles/article/details/131432802
Recommended