Listas vinculadas em Python

Listas vinculadas em Python

Nó Fonte: 3093495

Introdução

Uma lista vinculada é uma estrutura de dados que consiste em uma sequência de nós, cada um contendo um valor e uma referência ao próximo nó na sequência. Ao contrário dos arrays, as Listas Vinculadas não requerem alocação de memória contígua, tornando-as mais flexíveis e eficientes para determinadas operações. Neste artigo, exploraremos as vantagens e desvantagens das listas vinculadas e como implementá-las em Python.

Listas Ligadas

Índice

Vantagens e desvantagens das listas vinculadas

As listas vinculadas oferecem várias vantagens sobre outras estruturas de dados. Em primeiro lugar, permitem a inserção e eliminação eficiente de elementos, pois requerem apenas a atualização das referências dos nós vizinhos. Isso torna o LinkedLists ideal para cenários onde são esperadas modificações frequentes. Além disso, LinkedLists podem aumentar ou diminuir dinamicamente de tamanho, ao contrário dos arrays, que têm um tamanho fixo.

No entanto, as listas vinculadas também apresentam algumas desvantagens. Ao contrário dos arrays, as listas vinculadas não suportam acesso aleatório a elementos, o que significa que acessar um elemento em um índice específico requer percorrer a lista desde o início. Isso pode resultar em desempenho mais lento para determinadas operações. Além disso, as Listas Vinculadas requerem memória extra para armazenar as referências aos próximos nós, o que pode ser ineficiente para pequenos conjuntos de dados.

Implementando listas vinculadas em Python

Python fornece uma maneira flexível e intuitiva de implementar listas vinculadas. Existem três tipos principais de listas vinculadas: lista vinculada individualmente, lista duplamente vinculada e lista vinculada circular. Vamos explorar cada um deles em detalhes.

tipo de listas vinculadas

Lista encadeada individualmente

Uma lista vinculada individualmente consiste em nós onde cada nó contém um valor e uma referência ao próximo nó na sequência. Veja como você pode criar uma lista vinculada individualmente em Python:

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

class Linked List:
    def __init__(self):
        self.head = None

Criando uma lista vinculada individualmente

Para criar uma lista vinculada individualmente, precisamos definir uma classe Node que represente cada nó da lista. Cada nó contém um valor e uma referência para o próximo nó. A classe Linked List serve como contêiner para os nós, com o atributo head apontando para o primeiro nó da lista.

Inserindo nós em uma lista vinculada individualmente

A inserção de nós em uma lista vinculada individualmente envolve a atualização das referências dos nós vizinhos. Aqui está um exemplo de inserção de um nó no início da lista:

def insert_at_beginning(self, value):
    new_node = Node(value)
    new_node.next = self.head
    self.head = new_node

Excluindo nós de uma lista vinculada individualmente

A exclusão de nós de uma lista vinculada individualmente requer a atualização das referências dos nós vizinhos. Aqui está um exemplo de exclusão de um nó com um valor específico:

def delete_node(self, value):
    current = self.head
    if current.value == value:
        self.head = current.next
    else:
        while current.next:
            if current.next.value == value:
                current.next = current.next.next
                break
            current = current.next

Pesquisando em uma lista vinculada individualmente

Procurar um valor específico em uma lista vinculada individualmente envolve percorrer a lista até que o valor seja encontrado ou o final da lista seja alcançado. Aqui está um exemplo de pesquisa de um valor:

def search(self, value):
    current = self.head
    while current:
        if current.value == value:
            return True
        current = current.next
    return False

Revertendo uma lista vinculada individualmente

A reversão de uma lista vinculada individualmente requer a atualização das referências de cada nó para apontar para o nó anterior. Aqui está um exemplo de reversão de uma lista vinculada individualmente:

def reverse(self):
    previous = None
    current = self.head
    while current:
        next_node = current.next
        current.next = previous
        previous = current
        current = next_node
    self.head = previous

Lista duplamente vinculada

Uma lista duplamente vinculada é semelhante a uma lista vinculada individualmente, mas cada nó contém uma referência ao próximo nó e ao nó anterior na sequência. Isso permite uma travessia eficiente em ambas as direções. Veja como você pode criar uma lista duplamente vinculada em Python:

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None
        self.previous = None

class DoublyLinked List:
    def __init__(self):
        self.head = None

Criando uma lista duplamente vinculada

Para criar uma lista duplamente vinculada, definimos uma classe Node que contém um valor, uma referência ao próximo nó e uma referência ao nó anterior. A classe DoublyLinked List serve como contêiner para os nós, com o atributo head apontando para o primeiro nó da lista.

Inserindo nós em uma lista duplamente vinculada

A inserção de nós em uma lista duplamente vinculada envolve a atualização das referências dos nós vizinhos. Aqui está um exemplo de inserção de um nó no início da lista:

def insert_at_beginning(self, value):
    new_node = Node(value)
    if self.head:
        self.head.previous = new_node
    new_node.next = self.head
    self.head = new_node

Excluindo nós de uma lista duplamente vinculada

A exclusão de nós de uma lista duplamente vinculada requer a atualização das referências dos nós vizinhos. Aqui está um exemplo de exclusão de um nó com um valor específico:

def delete_node(self, value):
    current = self.head
    if current.value == value:
        self.head = current.next
        if self.head:
            self.head.previous = None
    else:
        while current.next:
            if current.next.value == value:
                current.next = current.next.next
                if current.next:
                    current.next.previous = current
                break
            current = current.next

Pesquisando em uma lista duplamente vinculada

Procurar um valor específico em uma lista duplamente vinculada envolve percorrer a lista em qualquer direção até que o valor seja encontrado ou o final da lista seja alcançado. Aqui está um exemplo de pesquisa de um valor:

def search(self, value):
    current = self.head
    while current:
        if current.value == value:
            return True
        current = current.next
    return False

Invertendo uma lista duplamente vinculada

A reversão de uma lista duplamente vinculada requer a atualização das referências de cada nó para trocar os ponteiros seguinte e anterior. Aqui está um exemplo de reversão de uma lista duplamente vinculada:

def reverse(self):
    current = self.head
    while current:
        next_node = current.next
        current.next = current.previous
        current.previous = next_node
        if not next_node:
            self.head = current
        current = next_node

Lista Circular Ligada

Uma lista vinculada circular é uma variação de uma lista vinculada individualmente, onde o último nó aponta de volta para o primeiro nó, criando uma estrutura circular. Isso permite uma travessia eficiente de qualquer nó da lista. Veja como você pode criar uma lista vinculada circular em Python:

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

class CircularLinked List:
    def __init__(self):
        self.head = None

Criando uma lista vinculada circular

Para criar uma lista vinculada circular, definimos uma classe Node que contém um valor e uma referência para o próximo nó. A classe CircularLinked List serve como contêiner para os nós, com o atributo head apontando para o primeiro nó da lista. Além disso, a próxima referência do último nó é definida como a cabeça, criando uma estrutura circular.

Inserindo nós em uma lista vinculada circular

A inserção de nós em uma lista vinculada circular envolve a atualização das referências dos nós vizinhos. Aqui está um exemplo de inserção de um nó no início da lista:

def insert_at_beginning(self, value):
    new_node = Node(value)
    if not self.head:
        self.head = new_node
        new_node.next = self.head
    else:
        current = self.head
        while current.next != self.head:
            current = current.next
        current.next = new_node
        new_node.next = self.head
        self.head = new_node

Excluindo nós de uma lista vinculada circular

A exclusão de nós de uma lista vinculada circular requer a atualização das referências dos nós vizinhos. Aqui está um exemplo de exclusão de um nó com um valor específico:

def delete_node(self, value):
    if not self.head:
        return
    current = self.head
    if current.value == value:
        while current.next != self.head:
            current = current.next
        if current == self.head:
            self.head = None
        else:
            current.next = self.head.next
            self.head = self.head.next
    else:
        previous = None
        while current.next != self.head:
            previous = current
            current = current.next
            if current.value == value:
                previous.next = current.next
                break

Pesquisando em uma lista vinculada circular

Procurar um valor específico em uma lista vinculada circular envolve percorrer a lista até que o valor seja encontrado ou toda a lista seja percorrida. Aqui está um exemplo de pesquisa de um valor:

def search(self, value):
    if not self.head:
        return False
    current = self.head
    while True:
        if current.value == value:
            return True
        current = current.next
        if current == self.head:
            break
    return False

Revertendo uma lista vinculada circular

A reversão de uma lista vinculada circular requer a atualização das referências de cada nó para reverter a estrutura circular. Aqui está um exemplo de reversão de uma lista vinculada circular:

def reverse(self):
    if not self.head:
        return
    previous = None
    current = self.head
    next_node = current.next
    while True:
        current.next = previous
        previous = current
        current = next_node
        next_node = next_node.next
        if current == self.head:
            break
    self.head = previous

Operações comuns em listas vinculadas

Listas vinculadas oferecem suporte a várias operações comuns que podem ser executadas nos elementos. Vamos explorar algumas dessas operações:

Acessando elementos em uma lista vinculada

Para acessar os elementos de uma Lista Vinculada, podemos percorrer a lista começando pelo nó principal e passar para o próximo nó até chegar à posição desejada. Aqui está um exemplo de acesso a um elemento em um índice específico:

def get_element(self, index):
    current = self.head
    count = 0
    while current:
        if count == index:
            return current.value
        count += 1
        current = current.next
    raise IndexError("Index out of range")

Modificando elementos em uma lista vinculada

Modificar elementos em uma lista vinculada envolve percorrer a lista para encontrar o elemento desejado e atualizar seu valor. Aqui está um exemplo de modificação de um elemento em um índice específico:

def modify_element(self, index, new_value):
    current = self.head
    count = 0
    while current:
        if count == index:
            current.value = new_value
            return
        count += 1
        current = current.next
    raise IndexError("Index out of range")

Encontrando o comprimento de uma lista vinculada

Para encontrar o comprimento de uma lista vinculada, podemos percorrer a lista e contar o número de nós. Aqui está um exemplo de como encontrar o comprimento de uma lista vinculada:

def get_length(self):
    current = self.head
    count = 0
    while current:
        count += 1
        current = current.next
    return count

Verificando se uma lista vinculada está vazia

Para verificar se uma lista vinculada está vazia, podemos simplesmente verificar se o nó principal é Nenhum. Aqui está um exemplo de como verificar se uma lista vinculada está vazia:

def is_empty(self):
    return self.head is None

Concatenando listas vinculadas

Para concatenar duas listas vinculadas, podemos percorrer a primeira lista para encontrar o último nó e atualizar sua próxima referência para o início da segunda lista. Aqui está um exemplo de concatenação de duas listas vinculadas:

def concatenate(self, other_list):
    if not self.head:
        self.head = other_list.head
    else:
        current = self.head
        while current.next:
            current = current.next
        current.next = other_list.head

Lista vinculada vs. matriz

Listas vinculadas e matrizes são estruturas de dados comumente usadas, mas possuem características diferentes que as tornam adequadas para diferentes cenários. Vamos comparar listas vinculadas e arrays em termos de eficiência de memória, eficiência de inserção e exclusão e eficiência de acesso aleatório.

Eficiência de memória

Listas vinculadas são mais eficientes em termos de memória do que matrizes porque não requerem alocação de memória contígua. Cada nó em uma lista vinculada precisa apenas armazenar o valor e uma referência para o próximo nó, enquanto os arrays precisam alocar memória para todos os elementos, mesmo que não sejam usados.

Eficiência de inserção e exclusão

As listas vinculadas são excelentes em operações de inserção e exclusão, especialmente quando elementos são frequentemente adicionados ou removidos do meio da lista. Inserir ou excluir um elemento em uma lista vinculada requer apenas a atualização das referências dos nós vizinhos, enquanto os arrays podem exigir a mudança de elementos para acomodar a mudança.

Eficiência de acesso aleatório

Os arrays fornecem acesso aleatório eficiente aos elementos com base em seus índices, pois permitem o endereçamento direto da memória. Por outro lado, as listas vinculadas exigem percorrer a lista desde o início para acessar um elemento em um índice específico, resultando em desempenho mais lento para operações de acesso aleatório.

Escolhendo a estrutura de dados correta

A escolha entre listas vinculadas e arrays depende dos requisitos específicos da aplicação. Se forem esperadas modificações frequentes e redimensionamento dinâmico, as Listas Vinculadas são uma escolha melhor. Por outro lado, se o acesso aleatório e a eficiência da memória são cruciais, os arrays são mais adequados.

Aplicativos de lista vinculada

Agora que temos uma boa compreensão das listas vinculadas e de como elas funcionam, vamos explorar algumas das aplicações práticas onde as listas vinculadas podem ser usadas de forma eficaz.

Lista Ligada

Você também pode se inscrever em nosso Cursos Livres Hoje!

Implementando Pilhas e Filas

Uma das aplicações mais comuns de listas vinculadas é a implementação de pilhas e filas. Tanto as pilhas quanto as filas são tipos de dados abstratos que podem ser facilmente implementados usando listas vinculadas.

Uma pilha é uma estrutura de dados que segue o princípio Last-In-First-Out (LIFO). Os elementos são adicionados e removidos da mesma extremidade, conhecida como topo da pilha. As listas vinculadas fornecem uma maneira eficiente de implementar pilhas, pois podemos facilmente adicionar ou remover elementos do início da lista.

Aqui está um exemplo de implementação de uma pilha usando uma lista vinculada em Python:

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class Stack:
    def __init__(self):
        self.head = None

    def push(self, data):
        new_node = Node(data)
        new_node.next = self.head
        self.head = new_node

    def pop(self):
        if self.head is None:
            return None
        popped = self.head.data
        self.head = self.head.next
        return popped

Uma fila, por outro lado, segue o princípio First-In-First-Out (FIFO). Os elementos são adicionados em uma extremidade, conhecida como traseira, e removidos na outra extremidade, conhecida como frontal. Listas vinculadas também podem ser usadas para implementar filas de forma eficiente.

Aqui está um exemplo de implementação de uma fila usando uma lista vinculada em Python:

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class Queue:
    def __init__(self):
        self.front = None
        self.rear = None

    def enqueue(self, data):
        new_node = Node(data)
        if self.rear is None:
            self.front = new_node
            self.rear = new_node
        else:
            self.rear.next = new_node
            self.rear = new_node

    def dequeue(self):
        if self.front is None:
            return None
        dequeued = self.front.data
        self.front = self.front.next
        if self.front is None:
            self.rear = None
        return dequeued

Manipulando Grandes Conjuntos de Dados

As listas vinculadas também são úteis ao lidar com grandes conjuntos de dados. Ao contrário dos arrays, as listas vinculadas não requerem alocação de memória contígua. Isso significa que as listas vinculadas podem lidar com eficiência com conjuntos de dados de tamanhos variados, sem a necessidade de redimensionamento ou realocação.

Por exemplo, digamos que temos um conjunto de dados de milhões de registros e precisamos realizar operações como inserção, exclusão ou travessia. Usar um array para esta tarefa pode ser ineficiente, pois requer a mudança de elementos ao inserir ou excluir. Porém, com uma lista vinculada, podemos facilmente inserir ou excluir elementos atualizando os ponteiros, resultando em operações mais rápidas.

Algoritmos de travessia de gráfico

Algoritmos de travessia de gráfico, como pesquisa em largura (BFS) e pesquisa em profundidade (DFS), também podem ser implementados usando listas vinculadas. Na travessia de grafos, visitamos cada vértice ou nó em um grafo em uma ordem específica.

Listas vinculadas podem ser usadas para representar a lista de adjacências de um gráfico, onde cada nó na lista vinculada representa um vértice e seus vértices adjacentes são armazenados como nós de lista vinculada. Esta representação permite a travessia e exploração eficiente do gráfico.

Representação Polinomial

Listas vinculadas podem ser usadas para representar polinômios de forma eficiente. Em matemática, polinômios são expressões que consistem em variáveis ​​e coeficientes. Cada termo em um polinômio pode ser representado como um nó em uma lista vinculada, onde o coeficiente e o expoente são armazenados como dados.

Usando listas vinculadas, podemos realizar facilmente operações em polinômios, como adição, subtração e multiplicação. Os nós podem ser manipulados para realizar essas operações, resultando em uma representação concisa e eficiente de polinômios.

Listas de reprodução de música e vídeo

As listas vinculadas são comumente usadas para implementar listas de reprodução em players de música e vídeo. Cada música ou vídeo pode ser representado como um nó em uma lista vinculada, onde os dados contêm informações sobre o arquivo de mídia e o ponteiro aponta para a próxima música ou vídeo da lista de reprodução.

O uso de listas vinculadas para playlists permite uma navegação fácil entre músicas ou vídeos. Podemos facilmente adicionar ou remover músicas da lista de reprodução atualizando os ponteiros, proporcionando uma experiência de usuário perfeita.

Conclusão

Concluindo, as listas vinculadas são estruturas de dados versáteis que encontram aplicações em vários domínios. Eles podem ser usados ​​para implementar pilhas e filas, lidar com grandes conjuntos de dados, realizar travessia de gráficos, representar polinômios e criar listas de reprodução. As listas vinculadas fornecem soluções eficientes para esses problemas, aproveitando sua alocação dinâmica de memória e fácil manipulação de nós.

Ao compreender as aplicações das listas vinculadas, podemos tomar decisões informadas ao escolher estruturas de dados para nossos programas. Seja gerenciando dados, implementando algoritmos ou criando interfaces amigáveis, as listas vinculadas oferecem uma ferramenta valiosa no kit de ferramentas do programador.

Portanto, da próxima vez que você encontrar um problema que exija inserção, exclusão ou travessia eficiente, considere usar listas vinculadas para simplificar sua solução e otimizar seu código.

Você também pode ler mais artigos relacionados às listas Python aqui:

Perguntas Frequentes

Q1: O que é uma lista vinculada?

R: Uma lista vinculada é uma estrutura de dados que consiste em nós, onde cada nó contém um valor e uma referência (ou link) para o próximo nó na sequência.

Q2: Quais são as vantagens de usar listas vinculadas?

R: As listas vinculadas oferecem operações eficientes de inserção e exclusão, redimensionamento dinâmico e não requerem alocação de memória contígua.

Q3: Quais são as desvantagens das listas vinculadas?

R: As listas vinculadas não possuem acesso aleatório, exigindo travessia para acesso ao elemento. Eles também consomem memória extra para armazenar referências, o que pode ser ineficiente para pequenos conjuntos de dados.

Q4: Quais são os principais tipos de listas vinculadas em Python?

R: Os principais tipos de listas vinculadas são lista vinculada individualmente, lista duplamente vinculada e lista vinculada circular.

P5: Em quais cenários as listas vinculadas são mais eficientes em termos de memória do que os arrays?

R: As listas vinculadas são mais eficientes em termos de memória do que os arrays ao lidar com redimensionamento dinâmico e inserções ou exclusões frequentes, pois não requerem alocação de memória contígua.

Carimbo de hora:

Mais de Análise Vidhya