[Structure des données] Liste chaînée circulaire de liste chaînée

Table des matières

Concepts et opérations de base :

Méthode et principe de mise en œuvre :

Scénarios d'application et précautions d'emploi :

Algorithme et analyse de complexité :

Comparaison avec d'autres structures de données :

PS : s'il y a des erreurs ou omissions, merci de me corriger


Concepts et opérations de base :

Une liste chaînée circulaire est une structure de liste chaînée spéciale, dont le dernier nœud pointe vers le premier nœud, formant une boucle, de sorte que le déclenchement à partir de n'importe quel nœud de la liste puisse trouver d'autres nœuds dans la liste. La liste chaînée circulaire peut être divisée en deux types : la liste chaînée circulaire unidirectionnelle et la liste chaînée circulaire bidirectionnelle. Ce qui suit est un diagramme schématique d’une liste circulaire à lien unique avec un nœud principal.

Diagramme schématique d'une liste circulaire à chaînage unique avec un pointeur de tête

Pour une liste chaînée circulaire, parfois changer le pointeur en pointeur de queue facilitera l'opération. La figure suivante est un diagramme schématique d'une liste chaînée unique circulaire avec un pointeur de queue.

Diagramme schématique d'une liste circulaire à chaînage unique avec pointeur de queue

Comme le montre le diagramme schématique ci-dessus, la relation logique entre l'utilisation d'un tableau circulaire pour représenter un tableau linéaire est la même que celle d'une liste à chaînage unique. La différence est que la valeur suivante du dernier élément ne peut pas être nulle, mais le premier dans la liste chaînée stockée.L'adresse de l'élément.

Voici les opérations courantes sur les listes chaînées circulaires :

  1. Nœud principal : le nœud situé avant le premier nœud de la liste chaînée circulaire ne stocke généralement pas de données et sa fonction principale est de faciliter le fonctionnement de la liste chaînée circulaire.

  2. Nœud de queue : le dernier nœud de la liste chaînée circulaire, dont le pointeur suivant pointe vers le nœud de tête.

  3. Insérer un élément : Pour insérer un nouvel élément à une position spécifiée, vous devez modifier le pointeur suivant du nœud précédent et le pointeur suivant du nouveau nœud.

  4. Supprimer l'élément : Pour supprimer l'élément à la position spécifiée, vous devez modifier le pointeur suivant du nœud précédent.

  5. Rechercher des éléments : parcourez la liste chaînée circulaire à partir du nœud principal jusqu'à ce que l'élément cible soit trouvé ou que la liste chaînée entière soit parcourue.

  6. Traversée d'éléments : la traversée de tous les éléments de la liste chaînée circulaire à partir du nœud principal peut être implémentée à l'aide d'une boucle while ou d'une boucle for.

  7. Inverser la liste chaînée : Inverser l’ordre de tous les nœuds dans la liste chaînée circulaire nécessite l’utilisation de trois pointeurs.

Il convient de noter que lors de l'exécution de diverses opérations, il est nécessaire d'assurer la continuité et l'ordre de la liste chaînée circulaire, c'est-à-dire que le pointeur suivant de chaque nœud pointe vers le nœud suivant et le pointeur suivant du dernier nœud pointe vers le nœud principal. Consultez le code C# ci-dessous pour les détails de mise en œuvre :

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

        }
    }

En bref, la liste chaînée circulaire est une structure de liste chaînée spéciale, largement utilisée dans les applications pratiques. Il convient de noter que lors de l'exécution de diverses opérations, il est nécessaire d'assurer la continuité et l'ordre de la liste chaînée circulaire pour éviter des problèmes tels que l'incohérence des données.

Méthode et principe de mise en œuvre :

Une liste chaînée circulaire est une structure spéciale de liste chaînée unidirectionnelle ou bidirectionnelle, le pointeur suivant du dernier nœud pointe vers le premier nœud, formant une boucle. J'ai présenté les méthodes et principes de mise en œuvre des listes chaînées circulaires unidirectionnelles et bidirectionnelles dans d'autres articles.

En bref, la liste chaînée circulaire est une structure de liste chaînée spéciale, et il faut veiller à assurer la continuité et l'ordre de la liste chaînée pendant la mise en œuvre.

Scénarios d'application et précautions d'emploi :

Une liste chaînée circulaire est une structure de liste chaînée spéciale, adaptée aux scénarios qui doivent parcourir des nœuds. Voici quelques scénarios courants d’application de listes chaînées circulaires :

  1. Problème de Joseph : Le problème de Joseph est un problème mathématique classique qui peut être implémenté avec une liste chaînée circulaire. Plus précisément, une liste chaînée circulaire peut être utilisée pour simuler un groupe de personnes formant un cercle, puis tuer chaque Mième personne à tour de rôle, et la dernière personne restante est la gagnante.

  2. Algorithme d'élimination du cache : dans l'algorithme d'élimination du cache, une liste chaînée circulaire peut être utilisée pour implémenter l'algorithme LRU (Least Récemment utilisé). Plus précisément, les données du cache peuvent être formées dans une liste chaînée circulaire par ordre de temps d'accès, et lorsque le cache est plein, supprimer les données qui n'ont pas été consultées depuis le plus longtemps.

  3. Effet de sélection : dans le développement d'une interface graphique, une liste chaînée circulaire peut être utilisée pour obtenir un effet de sélection, même si un morceau de texte est affiché de manière à défilement circulaire.

Lorsque vous utilisez une liste chaînée circulaire, vous devez faire attention aux points suivants :

  1. Le fonctionnement d’une liste chaînée circulaire est similaire à celui d’une liste chaînée normale, et il est relativement facile d’insérer, de supprimer et de rechercher. Cependant, il est nécessaire de faire attention à la continuité et à l'ordre de la liste chaînée circulaire pour éviter des problèmes tels que des boucles infinies ou des incohérences de données.

  2. Les nœuds de la liste chaînée circulaire doivent stocker un pointeur supplémentaire, ce qui prend donc plus de place que la liste chaînée ordinaire.

  3. Lors de l'utilisation d'une liste chaînée circulaire bidirectionnelle, il est nécessaire de prêter attention au traitement du nœud de tête et du nœud de queue, afin d'éviter la rupture de la liste chaînée en raison d'erreurs de fonctionnement.

En bref, une liste chaînée circulaire est une structure de liste chaînée spéciale, adaptée aux scénarios nécessitant un accès itératif aux nœuds. Dans les applications pratiques, il est nécessaire de sélectionner une structure de données appropriée en fonction de besoins et de scénarios spécifiques, et de prêter attention à ses caractéristiques et à ses précautions d'utilisation.

Algorithme et analyse de complexité :

Certains algorithmes de base pour les listes chaînées circulaires incluent :

  1. Insérer un élément à une position spécifiée : vous devez d'abord rechercher le nœud précédent à la position d'insertion, puis pointer le pointeur suivant du nouveau nœud vers le nœud à la position d'insertion, puis pointer le pointeur suivant du nœud précédent vers le nouveau nœud. La complexité temporelle est O(n).

  2. Supprimer un élément à une position spécifiée : vous devez rechercher le nœud précédent à la position de suppression, puis pointer son pointeur suivant vers le nœud suivant à la position de suppression. La complexité temporelle est O(n).

  3. Recherche d'éléments : il est nécessaire de parcourir la liste chaînée circulaire à partir du nœud principal jusqu'à ce que l'élément cible soit trouvé ou que la liste chaînée entière soit parcourue. La complexité temporelle est O(n).

  4. Traversée d'éléments : la traversée de tous les éléments de la liste chaînée circulaire à partir du nœud principal peut être implémentée à l'aide d'une boucle while ou d'une boucle for. La complexité temporelle est O(n).

  5. Inverser la liste chaînée : Inverser l’ordre de tous les nœuds dans la liste chaînée circulaire nécessite l’utilisation de trois pointeurs. La complexité temporelle est O(n).

Il est à noter que dans la liste chaînée circulaire, les opérations d'insertion et de suppression doivent en outre prendre en compte la modification du pointeur suivant du dernier nœud et du pointeur précédent du premier nœud. Dans le même temps, en raison de la nature particulière de la liste chaînée circulaire, lors du parcours et de la recherche d'éléments, il est nécessaire de déterminer si la condition de fin de boucle est remplie.

En bref, la liste chaînée circulaire est une structure de liste chaînée particulière.Lors de la mise en œuvre de diverses opérations, il est nécessaire de prêter attention à sa continuité et à son ordre, et de gérer les situations particulières en conséquence. Dans les applications pratiques, il est nécessaire de sélectionner des algorithmes et des structures de données appropriés en fonction de besoins et de scénarios spécifiques, et de faire des compromis.

Comparaison avec d'autres structures de données :

La liste chaînée circulaire est une structure de liste chaînée spéciale, qui présente les avantages et les inconvénients suivants par rapport aux autres structures de données :

  1. Par rapport aux listes chaînées ordinaires, les listes chaînées circulaires sont plus flexibles dans leur fonctionnement et peuvent réaliser des fonctions telles que l'accès itératif et le parcours. Cependant, dans les opérations telles que l'insertion, la suppression et la recherche, sa complexité temporelle est la même que celle d'une liste chaînée normale.

  2. Par rapport aux tableaux, les listes chaînées circulaires peuvent être étendues de manière dynamique et prendre en charge des opérations d'insertion et de suppression efficaces. Cependant, lors de l’accès aléatoire à des éléments, sa complexité temporelle est élevée et il n’est pas aussi efficace qu’un tableau.

  3. Par rapport aux piles et aux files d'attente, les listes chaînées circulaires peuvent stocker plus d'éléments et prendre en charge des méthodes d'accès plus flexibles. Cependant, lors de l'insertion et de la suppression d'éléments, sa complexité temporelle est relativement élevée et il ne convient pas aux opérations fréquentes de ce type.

  4. Comparées aux structures de données complexes telles que les arbres et les graphiques, les listes chaînées circulaires sont simples à mettre en œuvre, faciles à comprendre et à maintenir. Cependant, lors de la recherche et du parcours d’une grande quantité de données, sa complexité temporelle est élevée, ce qui ne convient pas à de tels scénarios d’application.

  5. Par rapport à la table de hachage, l'efficacité de recherche de la liste chaînée circulaire est faible et elle ne prend pas en charge les opérations d'insertion et de suppression rapides. Cependant, sa mise en œuvre est simple et il n’est pas nécessaire de résoudre des problèmes tels que les collisions de hachage.

En bref, une liste chaînée circulaire est une structure de liste chaînée spéciale, qui doit être sélectionnée en fonction de scénarios et d'exigences spécifiques dans des applications pratiques. Il convient de noter qu'il existe des compromis entre les différentes structures de données. Lors de leur utilisation, leurs avantages et inconvénients doivent être pris en compte de manière globale, et les structures de données et algorithmes appropriés doivent être sélectionnés.

PS : s'il y a des erreurs ou omissions, merci de me corriger

Je suppose que tu aimes

Origine blog.csdn.net/beenles/article/details/131432802
conseillé
Classement