Capítulo 05: Estruturas de Dados Lineares: Vetores, Listas, Pilhas e Filas
Capítulo 05: Estruturas de Dados Lineares: Vetores, Listas, Pilhas e Filas
Módulo 01: Fundamentos da Computação • Ciência da Computação Formal
🗺️ 1. Mapa Comparativo das Estruturas Lineares
flowchart LR
A["Estruturas Lineares"] --> VEC["Vetor / Array Contíguo
(Acesso O(1), Inserção O(n), Cache Friendly)"]
A --> LIST["Lista Encadeada
(Inserção O(1), Acesso O(n), Ponteiros)"]
A --> STACK["Pilha (LIFO)
(Push/Pop O(1), Call Stack)"]
A --> QUEUE["Fila (FIFO)
(Enqueue/Dequeue O(1), Buffers)"]
style VEC fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style LIST fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style STACK fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style QUEUE fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
🏛️ 2. Fundamentação Teórica e Análise de Complexidade
As estruturas de dados lineares organizam elementos sequencialmente em memória. A escolha da estrutura ideal depende da proporção entre leituras aleatórias e inserções/remoções dinâmicas.
| Estrutura de Dados | Acesso por Índice | Busca de Elemento | Inserção no Início | Inserção no Fim | Remoção no Início | Localidade de Cache |
|---|---|---|---|---|---|---|
| Vetor Estático (Array) | $O(1)$ | $O(n)$ | $O(n)$ | $O(1)$ | $O(n)$ | Excelente (Contíguo) |
| Vetor Dinâmico (List/Vector) | $O(1)$ | $O(n)$ | $O(n)$ | $O(1)$ Amortizado | $O(n)$ | Excelente |
| Lista Simplesmente Encadeada | $O(n)$ | $O(n)$ | $O(1)$ | $O(1)^*$ | $O(1)$ | Baixa (Nós espalhados) |
| Lista Duplamente Encadeada | $O(n)$ | $O(n)$ | $O(1)$ | $O(1)$ | $O(1)$ | Baixa |
| Pilha (LIFO - Stack) | $O(n)$ | $O(n)$ | $O(1)$ | — | $O(1)$ | Alta se baseada em array |
| Fila (FIFO - Queue) | $O(n)$ | $O(n)$ | — | $O(1)$ | $O(1)$ | Alta se circular em array |
2.1 Análise de Memória: Alocação Contígua vs Ponteiros Dispersos
- Vetores e Arrays Contíguos:
- Ocupam um bloco ininterrupto na memória RAM.
- O acesso a
A[i]é calculado aritmeticamente em tempo constante por hardware: \(\text{Endereço}(A[i]) = \text{EndereçoBase} + (i \times \text{TamanhoElemento})\) - Permitem aproveitamento máximo das linhas de cache L1/L2 do processador (Spatial Locality).
- Listas Encadeadas:
- Cada nó exige memória extra para armazenar 1 ou 2 ponteiros de 64 bits (8 a 16 bytes adicionais por valor).
- O percurso provoca constantes falhas de cache (Cache Misses), pois os nós estão espalhados pela Heap.
- Crescimento Geométrico de Vetores Dinâmicos:
- Quando a capacidade de um vetor dinâmico (
std::vectorem C++,List<T>em C#,listem Python) esgota, ele aloca um novo bloco com o dobro do tamanho ($2 \times$ ou $1.5 \times$) e copia os elementos. - Isso garante um custo Amortizado $O(1)$ por inserção, pois o custo pesado da cópia $O(n)$ ocorre com frequência exponencialmente decrescente.
- Quando a capacidade de um vetor dinâmico (
💻 3. Implementação de Lista Duplamente Encadeada com Sentinela em Python
class Node:
def __init__(self, val):
self.val = val
self.prev = None
self.next = None
class DoublyLinkedList:
def __init__(self):
# Uso de nós sentinela para eliminar checagens de ponteiro nulo (Clean Code)
self.head = Node(None)
self.tail = Node(None)
self.head.next = self.tail
self.tail.prev = self.head
self.size = 0
def append(self, val):
"""Insere no final em O(1) tempo."""
new_node = Node(val)
penultimo = self.tail.prev
penultimo.next = new_node
new_node.prev = penultimo
new_node.next = self.tail
self.tail.prev = new_node
self.size += 1
def prepend(self, val):
"""Insere no início em O(1) tempo."""
new_node = Node(val)
primeiro = self.head.next
self.head.next = new_node
new_node.prev = self.head
new_node.next = primeiro
primeiro.prev = new_node
self.size += 1
def to_list(self):
result = []
curr = self.head.next
while curr != self.tail:
result.append(curr.val)
curr = curr.next
return result
if __name__ == "__main__":
lista = DoublyLinkedList()
lista.append(10)
lista.append(20)
lista.prepend(5)
print(f"Lista Duplamente Encadeada: {lista.to_list()} (Tamanho: {lista.size})")
assert lista.to_list() == [5, 10, 20], "Falha estrutural na lista!"