数组、链表、栈与队列

选择数据结构之前,先列出最频繁的操作:随机访问、查找、插入,还是按顺序取出?

数组与链表

操作数组模型链表模型
按位置访问O(1)O(n)
查找指定值O(n)O(n)
中间插入通常需要移动元素,O(n)已知插入位置的前驱节点时为 O(1)
内存特点连续存储,局部性较好节点分散,存在指针开销

这里的数组指经典连续存储模型。具体语言的动态数组可能还有扩容、稀疏存储等实现细节。

区分定位与修改

链表插入可以是常数时间,但“先找到第 k 个节点”的过程仍然需要线性时间。

栈:后进先出

栈只在一端压入和弹出元素,适合括号匹配、撤销操作和深度优先遍历。

const stack: number[] = [];
stack.push(10);
stack.push(20);
console.log(stack.pop()); // 20

队列:先进先出

队列从一端加入、另一端取出,适合任务排队和广度优先遍历。以下用头指针避免每次取出时移动数组内容:

const queue: number[] = [];
let head = 0;
queue.push(10, 20);
if (head < queue.length) {
  console.log(queue[head++]); // 10
}

这种简单写法不会释放已经消费的数组槽位。长期运行的队列应考虑循环缓冲区,或定期压缩存储。

选择原则

需要频繁按下标读取时,优先考虑数组;需要后进先出或先进先出语义时,使用栈或队列接口,让代码直接表达意图。