数组
数组按下标组织同类元素,适合随机访问和顺序扫描。本文负责下标、有效长度、插入删除和扩缩容。
1. 面试核心
先记住这几条,再读正文2. 下标与连续槽位
等宽槽位可用 起始地址 + 下标 × 槽位大小 定位。访问第 100 项无需经过前 99 项。连续访问槽位有利于 CPU 缓存利用。引用所指对象的布局则由运行时管理。
本文采用连续槽位的数组实现模型。Java 语言规范定义下标与类型,未规定物理布局。
int[] values = {4, 7, 9};
values[1] = 8;
System.out.println(values[1]); // 8
写入替换了下标 1 的值,长度仍为 3。Java 下标从 0 开始,下标 3 会越界。动态容器另用 size 记录有效元素数。业务值 0 也可以是有效元素。
3. 插入与扩容
在 [4,8,9] 的下标 1 插入 6,需要将 8、9 后移。容量已满时,还需分配更大的数组。保序删除则将后缀前移。
容量每次翻倍时,复制次数构成等比数列。追加 n 项的累计复制量为 O(n)。因此,尾部追加摊销 O(1),单次扩容为 O(n)。每次只扩一格会累计复制 O(n²) 项。
3.1 扩缩容阈值
扩容与缩容应留出间隔。例如满时翻倍,使用量降到四分之一时减半。缩容后数组仍有一半空位。
若使用量刚到一半就减半,数组会立即变满。交替插入和删除便会反复复制。预留阈值间隔能让两次迁移间积累足够多的操作。
4. 使用边界
数组适合固定大小表格和批量扫描。中间修改的成本包括定位与移动。链表定位插入点最坏也需 O(n)。
允许改变顺序时,可用末尾元素覆盖待删位置。已知下标的删除因此可达 O(1)。随后减少 size,并清空多余的引用槽位。保留下来的外部下标也需同步更新。
5 · 面试题卡片
本页题目与参考回答
5.1 数组保序删除为何是 O(n),何时可降到 O(1)?
考察重点:顺序、定位与移动的成本。
相关内容:使用边界。
参考回答
保序删除最坏需前移 n−1 项,时间为 O(n)。
已知下标且允许乱序时,可用末项覆盖。再减少有效长度,操作为 O(1)。若需先按值查找,总时间仍最坏为 O(n)。
5.2 倍增扩容为何摊销 O(1),缩容阈值为何要留间隔?
考察重点:累计复制量与迁移频率。
相关内容:插入与扩容。
参考回答
倍增时,n 次追加的累计复制量为 O(n)。
均摊到每次追加就是 O(1)。缩容阈值留出间隔,可避免交替增删触发反复迁移。摊销结论约束操作序列,单次迁移仍需 O(n)。