1. 题面还原:堆内存申请到底在做什么
我在刷华为OD机试真题的时候遇到这道“堆内存申请”,题目名称里虽然带了“堆”,但实际不是让你写二叉堆,而是考察操作系统里的堆内存分配逻辑。它还有一个更直白的叫法:堆内存最佳分配,要求你按照最佳适配算法,从当前可用的连续空闲内存里,挑一块最合适的区域来满足一次内存申请。
先说清楚我备考时刷到的这版题面,避免后面对不上:
一个连续堆内存的总大小为 capacity,地址从 0 开始编号。现在已经有若干段内存被占用,每段用“起始地址 + 占用长度”表示。剩余没有被占用的区域都是空闲的。现在需要申请 need 字节的连续内存,要求按如下规则分配:
- 在所有长度不小于 need 的空闲区域中,选一个长度最小的。
- 如果长度相同,选起始地址最小的。
- 找到之后,返回该空闲区域的起始地址。
- 如果没有任何空闲区域能放下 need 字节,返回 -1。
这是内存分配算法里的 classic 问题:最佳适配(Best Fit)。在操作系统课程里,它和首次适配、最坏适配并称三种经典分区分配算法。OD机试把这个问题搬到考题里,其实就是在考你能不能把一个简单的内存分配模型用代码实现出来。
用一个具体的例子来理解最合适。假设内存总大小 capacity = 20,已经被占用的区间如下:
text复制20
2
2 3
8 5
4
其中:
20表示总内存 20 字节;2表示有 2 个已占用区域;2 3表示从地址 2 开始连续占用 3 个字节;8 5表示从地址 8 开始连续占用 5 个字节;4表示本次申请 4 字节。
当然,华为OD真题里很多版本会把总大小固定写为 100,地址范围就是 0 到 99,这并不影响算法思路。
将占用地块转成区间表示,更方便处理边界。我建议全程序统一使用左闭右开区间 [start, end)。这样一段长度为 len 的占用内存,如果起始地址是 start,对应的占用区间就是 [start, start + len)。区间的长度永远是 end - start,不用再反复“+1”“-1”,能省掉大量边界 bug。
上述案例中的占用区间就是:
[2, 5),也就是地址 2、3、4 被占用;[8, 13),也就是地址 8、9、10、11、12 被占用。
那么剩余空闲区域包括:
[0, 2),长度 2;[5, 8),长度 3;[13, 20),长度 7。
现在申请 4 字节。长度不小于 4 的空闲区域只有 [13, 20),所以最终输出地址是 13。
这道题的核心考点有三块:
第一,能不能理解连续内存和空闲区域的关系。内存不是一个一个独立小格子,而是一段连续地址。只有未被占用的连续区域才能分配给申请者。
第二,能不能把“最佳适配”四个字准确翻译成判断条件。很多人写成“找到第一个长度够大的空闲区”,那就变成了首次适配,题目必挂。这
