一、题目描述
请实现一个简易内存池,根据请求命令完成内存分配和释放。
内存池支持两种操作命令,REQUEST和RELEASE,其格式为:
REQUEST=请求的内存大小 表示请求分配指定大小内存,如果分配成功,返回分配到的内存首地址;如果内存不足,或指定的大小为0,则输出error。
RELEASE=释放的内存首地址 表示释放掉之前分配的内存,释放成功无需输出,如果释放不存在的首地址则输出error。
注意:
- 内存池总大小为100字节。
- 内存池地址分配必须是连续内存,并优先从低地址分配。
- 内存释放后可被再次分配,已释放的内存在空闲时不能被二次释放。
- 不会释放已申请的内存块的中间地址。
- 释放操作只是针对首地址所对应的单个内存块进行操作,不会影响其它内存块。
二、输入输出描述
输入描述
- 第一行:整数 N , 表示操作命令的个数,取值范围:0 < N <= 100。
- 接下来N行:每行将给出一个操作命令,操作命令和参数之间用 "="分割。
输出描述
- 请求分配指定大小内存时,如果分配成功,返回分配到的内存首地址;如果内存不足,或指定的大小为0,则输出error
- 释放掉之前分配的内存时,释放成功无需输出,如果释放不存在的首地址则输出error。
三、示例
|----|-------------------------|
| 输入 | 2 REQUEST=10 REQUEST=20 |
| 输出 | 0 10 |
| 说明 | |
|----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 输入 | 5 REQUEST=10 REQUEST=20 RELEASE=0 REQUEST=20 REQUEST=10 |
| 输出 | 0 10 30 0 |
| 说明 | 第一条指令,申请地址0~9的10个字节内存,返回首地址0 第二条指令,申请地址10~29的20字节内存,返回首地址10 第三条指令,释放首地址为0的内存申请,0~9地址内存被释放,变为空闲,释放成功,无需输出 第四条指令,申请20字节内存,09地址内存连续空间不足20字节,往后查找到3049地址,返回首地址30 第五条指令,申请10字节,0~9地址内存空间足够,返回首地址0 |
四、解题思路
- 核心思想
采用首次适配 策略 + 有序链表管理已占用内存 + 区间交集判断,实现内存的申请与释放管理。核心是用有序链表维护已占用内存块(含边界),从地址 0 开始遍历查找首个可用空闲区间(无交集)完成申请,按起始地址精准匹配完成释放,异常场景输出 error。
- 问题本质分析
- 表层问题:按 REQUEST/RELEASE 指令模拟内存管理,输出申请地址或错误信息;
- 深层问题:
-
内存区间管理:本质是有序区间的插入(申请)、删除(释放)与冲突判断(交集),有序链表是高效的存储结构;
-
申请策略选择:首次适配(从 0 开始查找)实现简单,符合常规内存管理的基础逻辑,无需复杂排序;
-
边界控制:通过预设
[100,101]尾边界,限制最大可用地址,避免内存申请超出范围; -
异常处理:申请时无可用区间、释放时无对应起始地址,均需输出 error,保证逻辑的完整性。
-
核心逻辑
- 存储管理:用
LinkedList有序存储已占用内存块(含尾边界[100,101]),保持地址从小到大排序,方便遍历查找; - 内存申请:
- 首次适配:从地址 0 开始,计算待申请区间的结束地址;
- 冲突判断:通过
hasIntersection判断待申请区间与已占用区间是否无交集(无交集即可用); - 插入与更新:可用则插入区间并输出起始地址,不可用则更新查找起始地址,无可用区间输出 error;
- 内存释放:
- 精准匹配:遍历有序链表,寻找起始地址完全一致的已占用内存块;
- 删除与异常:找到则删除区间,未找到则输出 error;
- 区间判断:通过比较两个区间的起始、结束地址,快速判断是否相交,为申请提供可用依据。
-
步骤拆解
-
初始化准备
- 读取指令数量
n和所有指令,拆分为 "操作类型 - 参数" 二维数组; - 初始化
LinkedList存储已占用内存块,添加尾边界[100,101],限制最大可用地址。
- 读取指令数量
-
遍历处理每条指令按指令类型分支处理:
- 分支 A:内存申请(REQUEST)
- 解析申请大小
size,初始化查找起始地址start=0,申请失败标记flag=true; - 遍历已占用内存块:a. 计算待申请区间的结束地址
end=start+size-1,构建待申请区间[start, end];b. 调用hasIntersection判断与当前已占用区间是否无交集:- 无交集:插入待申请区间到链表,标记
flag=false,输出start,退出遍历; - 有交集:更新
start为当前已占用区间的结束地址 + 1,继续遍历;
- 无交集:插入待申请区间到链表,标记
- 若
flag=true(未找到可用区间),输出error;
- 分支 B:内存释放(RELEASE)
- 解析释放起始地址
addr,初始化释放失败标记flag=true; - 遍历已占用内存块,寻找起始地址等于
addr的区间:- 找到:删除该区间,标记
flag=false,退出遍历; - 未找到:继续遍历;
- 找到:删除该区间,标记
- 若
flag=true(未找到对应区间),输出error。
-
输出结果 按操作结果实时输出:申请成功输出起始地址,申请 / 释放失败输出
error,释放成功无额外输出。
五、代码实现
java
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
// 输入获取
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String[][] cmds = new String[n][2];
for (int i = 0; i < n; i++) cmds[i] = sc.next().split("=");
getResult(n, cmds);
}
// 算法入口
public static void getResult(int n, String[][] cmds) {
// used保存被占用的内存 [起始地址,结束地址],初始时有一个[100,101]作为尾边界限定
LinkedList<Integer[]> used = new LinkedList<>();
used.add(new Integer[] {100, 101});
for (String[] cmd : cmds) {
String key = cmd[0];
String val = cmd[1];
// 申请内存
if ("REQUEST".equals(key)) {
// 当指令为REQUEST时,对应值为要申请的内存的大小,即size
int size = Integer.parseInt(val);
// 我们默认从start=0位置开始检查可用内存区间
int start = 0;
boolean flag = true;
for (int i = 0; i < used.size(); i++) {
int end = start + size - 1;
// 要申请的内存区间
Integer[] range = {start, end};
// 检查要申请的内存区间和已占有的内存区间是否交叉
if (!hasIntersection(used.get(i), range)) {
// 若不存在交叉,则将申请区间加入used中
used.add(i, range);
flag = false;
// 并打印此时申请区间的起始位置
System.out.println(start);
break;
} else {
// 若存在交叉,则将变更要申请的内存区间的起始位置
start = used.get(i)[1] + 1;
}
}
// 一旦申请到内存,那么flag就会被赋值为false,否则就保持true,意味着每申请到内存,则打印error
if (flag) System.out.println("error");
}
// 释放内存
else {
// 当指令为RELEASE时,值为要释放内存的起始地址addr
int addr = Integer.parseInt(val);
boolean flag = true;
for (int i = 0; i < used.size(); i++) {
// 到已占有内存中找起始位置是addr的,找到则将该区间从used中删除,表示解除占用
if (used.get(i)[0] == addr) {
used.remove(i);
flag = false;
break;
}
}
// 一旦释放成功,则flag就会被置为false,否则就保持True,意味着没有内存释放,则打印error
if (flag) System.out.println("error");
}
}
}
// 判断两个区间是否存在交集
public static boolean hasIntersection(Integer[] range1, Integer[] range2) {
int s1 = range1[0];
int e1 = range1[1];
int s2 = range2[0];
int e2 = range2[1];
if (s1 == s2) return true;
else if (s1 < s2) return e1 >= s2;
else return e2 >= s1;
}
}