[Estrutura de dados] Lista vinculada circular de lista vinculada

Índice

Conceitos e operações básicas:

Método e princípio de implementação:

Cenários de aplicação e precauções de uso:

Análise de algoritmo e complexidade:

Comparação com outras estruturas de dados:

PS: Se houver algum erro ou omissão, corrija-me


Conceitos e operações básicas:

Uma lista vinculada circular é uma estrutura especial de lista vinculada, cujo último nó aponta para o primeiro nó, formando um loop, de modo que o acionamento de qualquer nó da lista possa encontrar outros nós na lista. A lista vinculada circular pode ser dividida em dois tipos: lista vinculada circular unidirecional e lista vinculada circular bidirecional. A seguir está um diagrama esquemático de uma lista circular unida individualmente com um nó líder.

Diagrama esquemático de uma lista circular encadeada individualmente com um ponteiro principal

Para uma lista vinculada circular, às vezes alterar o ponteiro para um ponteiro final tornará a operação mais fácil.A figura a seguir é um diagrama esquemático de uma lista vinculada circular única com um ponteiro final.

Diagrama esquemático de uma lista circular unida individualmente com ponteiro de cauda

Como pode ser visto no diagrama esquemático acima, a relação lógica de usar uma tabela circular para representar uma tabela linear é a mesma de uma lista vinculada individualmente. A diferença é que o próximo valor do último elemento não pode ser nulo, mas o primeiro na lista vinculada armazenada.O endereço do elemento.

A seguir estão operações comuns em listas vinculadas circulares:

  1. Nó principal: O nó anterior ao primeiro nó da lista vinculada circular, geralmente não armazena dados, e sua principal função é facilitar o funcionamento da lista vinculada circular.

  2. Nó final: O último nó da lista vinculada circular, cujo próximo ponteiro aponta para o nó principal.

  3. Inserir elemento: Para inserir um novo elemento em uma posição especificada, você precisa modificar o próximo ponteiro do nó anterior e o próximo ponteiro do novo nó.

  4. Excluir elemento: para excluir o elemento na posição especificada, você precisa modificar o próximo ponteiro do nó anterior.

  5. Encontrar elementos: percorra a lista vinculada circular a partir do nó principal até que o elemento de destino seja encontrado ou toda a lista vinculada seja percorrida.

  6. Atravessando elementos: A travessia de todos os elementos da lista vinculada circular a partir do nó principal pode ser implementada usando um loop while ou um loop for.

  7. Reverter a lista vinculada: Reverter a ordem de todos os nós na lista vinculada circular requer o uso de três ponteiros.

Ressalta-se que ao realizar diversas operações, é necessário garantir a continuidade e ordem da lista vinculada circular, ou seja, o próximo ponteiro de cada nó aponta para o próximo nó, e o próximo ponteiro do último nó aponta para o nó principal. Consulte o código C# abaixo para obter detalhes de implementação:

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

        }
    }

Em suma, a lista vinculada circular é uma estrutura especial de lista vinculada, amplamente utilizada em aplicações práticas. Ressalta-se que ao realizar diversas operações, é necessário garantir a continuidade e a ordem da lista vinculada circular para evitar problemas como inconsistência de dados.

Método e princípio de implementação:

Uma lista vinculada circular é uma estrutura especial de lista vinculada unidirecional ou bidirecional, o próximo ponteiro do último nó aponta para o primeiro nó, formando um ciclo. Introduzi os métodos e princípios de implementação de listas vinculadas circulares unidirecionais e bidirecionais em outros artigos.

Em suma, a lista vinculada circular é uma estrutura especial de lista vinculada e deve-se tomar cuidado para garantir a continuidade e a ordem da lista vinculada durante a implementação.

Cenários de aplicação e precauções de uso:

Uma lista vinculada circular é uma estrutura de lista vinculada especial, adequada para cenários que precisam iterar por meio de nós. A seguir estão alguns cenários comuns de aplicação de lista vinculada circular:

  1. Problema de Joseph: O problema de Joseph é um problema matemático clássico que pode ser implementado com uma lista vinculada circular. Especificamente, uma lista vinculada circular pode ser usada para simular um grupo de pessoas formando um círculo e, em seguida, matar cada M-ésima pessoa por vez, e a última pessoa restante é a vencedora.

  2. Algoritmo de eliminação de cache: No algoritmo de eliminação de cache, uma lista vinculada circular pode ser usada para implementar o algoritmo LRU (menos usado recentemente). Especificamente, os dados no cache podem ser formados em uma lista circular vinculada em ordem de tempo de acesso e, quando o cache estiver cheio, excluir os dados que não foram acessados ​​por mais tempo.

  3. Efeito letreiro: No desenvolvimento da interface gráfica, uma lista vinculada circular pode ser usada para obter o efeito letreiro, mesmo que um trecho de texto seja exibido em rolagem circular.

Ao usar uma lista vinculada circular, você precisa prestar atenção aos seguintes pontos:

  1. A operação de uma lista vinculada circular é semelhante à de uma lista vinculada normal e é relativamente fácil de inserir, excluir e pesquisar. Porém, é necessário estar atento à continuidade e ordem da lista vinculada circular para evitar problemas como loops infinitos ou inconsistências de dados.

  2. Os nós da lista vinculada circular precisam armazenar um ponteiro adicional, por isso ocupam mais espaço do que a lista vinculada comum.

  3. Ao utilizar uma lista vinculada circular bidirecional, é necessário prestar atenção ao processamento do nó principal e do nó final, para evitar a quebra da lista vinculada devido a erros de operação.

Resumindo, uma lista vinculada circular é uma estrutura especial de lista vinculada, adequada para cenários que requerem acesso iterativo aos nós. Em aplicações práticas, é necessário selecionar uma estrutura de dados adequada de acordo com necessidades e cenários específicos, e atentar para suas características e cuidados de uso.

Análise de algoritmo e complexidade:

Alguns algoritmos básicos para listas vinculadas circulares incluem:

  1. Inserir um elemento em uma posição especificada: você precisa primeiro encontrar o nó anterior na posição de inserção, depois apontar o próximo ponteiro do novo nó para o nó na posição de inserção e, em seguida, apontar o próximo ponteiro do nó anterior para o novo nó. A complexidade do tempo é O (n).

  2. Excluir um elemento em uma posição especificada: você precisa encontrar o nó anterior na posição de exclusão e, em seguida, apontar seu próximo ponteiro para o próximo nó na posição de exclusão. A complexidade do tempo é O (n).

  3. Encontrando elementos: É necessário percorrer a lista vinculada circular a partir do nó principal até que o elemento de destino seja encontrado ou toda a lista vinculada seja percorrida. A complexidade do tempo é O (n).

  4. Atravessando elementos: A travessia de todos os elementos da lista vinculada circular a partir do nó principal pode ser implementada usando um loop while ou um loop for. A complexidade do tempo é O (n).

  5. Reverter a lista vinculada: Reverter a ordem de todos os nós na lista vinculada circular requer o uso de três ponteiros. A complexidade do tempo é O (n).

Deve-se notar que na lista vinculada circular, as operações de inserção e exclusão precisam considerar adicionalmente a modificação do próximo ponteiro do último nó e do ponteiro anterior do primeiro nó. Ao mesmo tempo, devido à natureza especial da lista vinculada circular, ao percorrer e procurar elementos, é necessário julgar se a condição para o final do loop foi atendida.

Em suma, a lista circular ligada é uma estrutura especial de lista ligada.Ao implementar diversas operações, é necessário estar atento à sua continuidade e ordem, e lidar com situações especiais em conformidade. Em aplicações práticas, é necessário selecionar algoritmos e estruturas de dados apropriados de acordo com necessidades e cenários específicos, e fazer concessões e compromissos.

Comparação com outras estruturas de dados:

A lista vinculada circular é uma estrutura especial de lista vinculada, que apresenta as seguintes vantagens e desvantagens em comparação com outras estruturas de dados:

  1. Em comparação com as listas vinculadas comuns, as listas vinculadas circulares são mais flexíveis na operação e podem realizar funções como acesso iterativo e travessia. No entanto, em operações como inserção, exclusão e pesquisa, sua complexidade de tempo é a mesma de uma lista encadeada normal.

  2. Em comparação com matrizes, as listas vinculadas circulares podem ser expandidas dinamicamente e suportar operações eficientes de inserção e exclusão. Porém, ao acessar elementos aleatoriamente, sua complexidade de tempo é alta e não é tão eficiente quanto um array.

  3. Em comparação com pilhas e filas, as listas vinculadas circulares podem armazenar mais elementos e oferecer suporte a métodos de acesso mais flexíveis. Porém, ao inserir e excluir elementos, sua complexidade de tempo é relativamente alta e não é adequado para operações frequentes desse tipo.

  4. Em comparação com estruturas de dados complexas, como árvores e gráficos, as listas circulares vinculadas são simples de implementar, fáceis de entender e manter. Porém, ao pesquisar e percorrer uma grande quantidade de dados, sua complexidade de tempo é alta, o que não é adequado para tais cenários de aplicação.

  5. Em comparação com a tabela hash, a eficiência de pesquisa da lista vinculada circular é baixa e não suporta operações rápidas de inserção e exclusão. No entanto, a sua implementação é simples e não há necessidade de lidar com questões como colisões de hash.

Em suma, a lista vinculada circular é uma estrutura especial de lista vinculada, que precisa ser selecionada de acordo com cenários e requisitos específicos em aplicações práticas. Deve-se notar que existem compensações e compromissos entre diferentes estruturas de dados. Ao utilizá-las, suas vantagens e desvantagens devem ser consideradas de forma abrangente, e estruturas de dados e algoritmos apropriados devem ser selecionados.

PS: Se houver algum erro ou omissão, corrija-me

Acho que você gosta

Origin blog.csdn.net/beenles/article/details/131432802
Recomendado
Clasificación