Listas encadeadas
Acrescentar um valor na frente de uma lista encadeada muda dois ponteiros, tenha a lista 4 valores ou 4 milhões.
Valores que apontam para o próximo
Um array guarda os valores lado a lado, e é por isso que a frente é cara de mudar. Uma lista encadeada abandona essa regra. Cada valor vive num pequeno registro próprio chamado nó, e os nós podem ficar em qualquer lugar da memória. As notas do 6.006 do MIT descrevem um nó com dois campos: o item e next, um ponteiro para o nó seguinte. A lista em si só lembra o primeiro nó, chamado head (cabeça). O next do último nó é null, o que marca o fim.
Para ler a lista, você começa em head e segue os ponteiros next um por um, como seguir uma série de bilhetes em que cada um diz onde o próximo está escondido. Isso tem um custo. Não existe fórmula para o endereço do quarto nó, então pegar o valor na posição i exige visitar i + 1 nós. O MIT lista get at(i) de uma lista encadeada como O(n), enquanto o array fazia isso em um passo.
// um nó por valor: { value, next }
let head = null;
for (const v of [4, 30, 7, 12]) head = { value: v, next: head };
// percorrer a lista
for (let n = head; n !== null; n = n.next) console.log(n.value); // 12, 7, 30, 4Mudando a lista religando os nós
A vantagem aparece quando você muda a lista. Para pôr um valor novo na frente, crie um nó cujo next é o head atual e depois aponte head para o nó novo. Duas mudanças de ponteiro, e nada mais se move. Apagar o nó da frente é uma mudança: aponte head para head.next. Compare com o array, em que as mesmas operações moviam todos os valores.
No meio, o trabalho é achar o lugar. Para inserir na posição i, você anda até o nó logo antes dela (i visitas) e depois faz as mesmas duas mudanças de ponteiro: new.next = prev.next, depois prev.next = new. A ordem importa. Se você mudar prev.next primeiro, o resto da lista se perde, porque nada mais aponta para ele. No laboratório, um passo é um nó visitado ou um ponteiro alterado, então inserir na posição 2 de uma lista de 4 nós custa 2 visitas mais 2 mudanças, 4 passos.
Arraste para girar. Role ou faça pinça para dar zoom.
| Operação | Array (n valores) | Lista encadeada (n nós) |
|---|---|---|
| Pegar o valor na posição i | 1 | i + 1 visitas |
| Inserir na frente | n movimentos + 1 escrita | 2 mudanças de ponteiro |
| Apagar a frente | 1 leitura + (n − 1) movimentos | 1 mudança de ponteiro |
| Inserir na posição i | (n − i) movimentos + 1 escrita | i visitas + 2 mudanças |
| Achar um valor | até n leituras | até n visitas |
Quando usar
Uma lista encadeada é uma boa escolha quando a maior parte das mudanças acontece num lugar para o qual você já tem um ponteiro, como a frente, e você raramente precisa da posição i. Filas e pilhas podem ser construídas com listas encadeadas, assim como as cadeias dentro de uma tabela hash, na próxima unidade. Há variantes. Uma lista duplamente encadeada dá a cada nó também um ponteiro prev, para que você possa andar para trás e apagar um nó em um passo depois de tê-lo nas mãos. Guardar um ponteiro tail além de head deixa barato acrescentar no fim.
No JavaScript do dia a dia, você raramente escreve uma lista encadeada, porque os arrays são bem otimizados e os caches dos processadores modernos favorecem valores guardados lado a lado. A ideia importa mesmo assim: árvores, grafos e muitas estruturas de bibliotecas são construídos com nós e ponteiros exatamente desse jeito.
- Quais dois campos um nó de lista simplesmente encadeada guarda?
- item e next
- índice e valor
- esquerda e direita
- head e tail
- Lista 12 → 7 → 30 → 4. Quantos passos leva inserir um valor novo na frente (mudanças de ponteiro)?
- Mesma lista: inserir na posição 2 custa quantos passos (visitas mais mudanças de ponteiro)?
- Mesma lista: quantos nós uma busca por 30 visita?
- Mesma lista: quantos nós uma busca por 99 visita antes de chegar a null?
- Mesma lista: apagar o nó na posição 3 (o 4) custa quantos passos?
- Um array tem 1000 valores e espaço de sobra. Quantos passos leva inserir na frente?
- Qual trabalho combina mais com uma lista encadeada do que com um array?
- Ler muitas vezes o valor na posição 500
- Acrescentar e remover na frente muitas vezes
- Busca binária
- Ordenar por índice
- Ao inserir depois do nó p, qual ordem de passos preserva o resto da lista?
- p.next = new, depois new.next = p.next
- new.next = p.next, depois p.next = new
- Qualquer ordem funciona
- Apagar p primeiro
- Qual linguagem, escrita por volta de 1956 na RAND, introduziu o processamento de listas com células encadeadas?
- Fortran
- IPL
- C
- JavaScript
- Uma lista encadeada guarda cada valor num nó com um ponteiro next; a lista guarda head.
- Inserir ou apagar na frente muda 1 ou 2 ponteiros; nada se move.
- Chegar à posição i leva i + 1 visitas, então get at(i) é O(n).
- Defina new.next antes de religar o nó anterior.
Fontes deste curso (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 2 Data Structures (interface vs data structure, Sequence and Set interfaces, stack = insert_last/delete_last, queue = insert_last/delete_first, array, linked list, dynamic array, amortized Θ(1) append)
- 4 Hashing (chaining, pigeonhole collisions, chain length n/m)
- 6 Binary Trees Part 1 (height, traversal order, O(h) operations) and 7 Binary Trees Part 2: AVL (Adelson-Velsky and Landis, 1962) at ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes
- MDN Web Docs, Array.prototype.shift (shifts all values left by 1)
- Array.prototype.unshift, Map (insertion order, Map vs Object, spec requires sublinear average access) and Set (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
- ECMAScript 2025 Language Specification (tc39.es/ecma262)
- 24.1 Map Objects and 24.2 Set Objects ("must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements")
- Array.prototype.shift steps (Repeat, while k < len: move element k to k - 1)
- D. E. Knuth, The Art of Computer Programming vol. 3 (2nd ed. 1998) section 6.4 History (H. P. Luhn, IBM internal memorandum January 1953, hashing with chaining; digit pairs added mod 10, 31415926 -> 4548) via gwern.net copy, and IEEE Spectrum, "Hans Peter Luhn and the Birth of the Hashing Algorithm" (2018)
- "A brief history of the stack", SIGCIS (sigcis.org/files/A brief history.pdf: Bauer and Samelson applied for the patent in 1957, US 3,047,228 granted July 31, 1962; IPL list processing by Newell, Simon and Shaw at RAND 1956) and Wikipedia, Information Processing Language (Newell, Shaw, Simon, RAND and Carnegie Institute of Technology, about 1956).
Data de verificação: 2026-10-18