Publicado · en mejora
Guía de Estructuras de datos básicas · 6/6
Por ahora, este capítulo solo está disponible en inglés.
The structures in this guide sit underneath almost every program. Knowing how the standard libraries implement them explains many performance surprises and a few classic bugs.
set is also a hash table with open addressing.std::deque stores fixed-size chunks plus a map of chunk pointers, and std::unordered_map uses separate chaining with a default maximum load factor of 1.0.HashMap uses a power-of-two bucket array, a default load factor of 0.75, and converts a bucket's chain into a red-black tree once it holds more than 8 entries and the table has at least 64 buckets.| Structure | Typical uses |
|---|---|
| Stack | Function call stack, undo history, expression parsing, depth-first search |
| Queue | Job and message queues, breadth-first search, print spooling, rate limiting windows |
| Ring buffer | Audio and network buffers, log tails, producer and consumer pipes |
| Linked list | OS kernel lists, free lists in memory allocators, the recency order in LRU caches |
| Hash table | Caches, symbol tables in compilers, database hash joins and indexes, deduplication |
A good example of combining them is an LRU cache: a hash map finds an entry in O(1) and a doubly linked list keeps entries in recency order so the oldest can be evicted in O(1). Python's OrderedDict is exactly that pair:
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.data = OrderedDict()
def get(self, key):
if key not in self.data:
return None
self.data.move_to_end(key) # mark as most recently used
return self.data[key]
def put(self, key, value):
self.data[key] = value
self.data.move_to_end(key)
if len(self.data) > self.capacity:
self.data.popitem(last=False) # evict the least recently used
cache = LRUCache(2)
cache.put("a", 1); cache.put("b", 2); cache.get("a"); cache.put("c", 3)
print(list(cache.data)) # ['a', 'c'] -- 'b' was evictedlist.pop(0), ArrayList.remove(0) and Array.prototype.shift move every remaining element. Use a deque or a ring buffer for queues.equals and hashCode together and should not mutate the fields they use.dict, Map and LinkedHashMap preserve insertion order, but HashMap, HashSet, std::unordered_map and Python set do not. Code that happens to work on one input can break on another.std::vector reallocates, every pointer, reference and iterator into it becomes invalid. Call reserve up front or store indices.RuntimeError in Python and ConcurrentModificationException in Java.The Java pitfall in code: without hashCode, two equal keys land in different buckets and lookups fail. A record generates both methods consistently.
import java.util.HashMap;
public class KeyDemo {
static final class BadPoint {
final int x, y;
BadPoint(int x, int y) { this.x = x; this.y = y; }
@Override public boolean equals(Object o) {
return o instanceof BadPoint p && p.x == x && p.y == y;
}
// hashCode is missing: equal points get different hash codes
}
record Point(int x, int y) {} // equals and hashCode generated together
public static void main(String[] args) {
var bad = new HashMap<BadPoint, String>();
bad.put(new BadPoint(1, 2), "found");
var good = new HashMap<Point, String>();
good.put(new Point(1, 2), "found");
System.out.println(bad.get(new BadPoint(1, 2)) + " " + good.get(new Point(1, 2))); // almost always: null found
}
}And the C++ one: a pointer taken before a reallocation must not be used afterwards.
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{1, 2, 3};
v.reserve(100); // capacity fixed in advance: no reallocation below
int* first = &v[0];
for (int i = 0; i < 50; ++i) v.push_back(i);
std::cout << *first << ' ' << v.size() << ' ' << v.capacity() << '\n'; // 1 53 100
// without reserve, push_back may reallocate and 'first' would dangle
}Standard containers are carefully tuned versions of the structures in this guide: dynamic arrays with geometric growth, deques built from blocks, and hash tables with bounded load factors. Use them, choose them by the operations you need, and watch for the common traps: front removal from arrays, unstable keys, assumed ordering, dangling references and untrusted keys.
0 comentarios
Iniciar sesión · Inicia sesión para dejar un comentario.
Sé el primero en comentar.