2025 A卷 200分 题型
本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析;
并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式!
本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》
华为OD机试真题《简易内存池》:
目录
- 题目名称:简易内存池
- 题目描述
- Java
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入:
- 示例3输入:
- 综合分析
- python
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入:
- 示例3输入:
- 综合分析
- JavaScript
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入:
- 示例3输入:
- 综合分析
- C++
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入:
- 示例3输入:
- 综合分析
- C语言
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入:
- 示例3输入:
- 综合分析
- GO
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 示例1输入:
- 示例2输入:
- 示例3输入:
- 综合分析
- 更多内容:
题目名称:简易内存池
- 知识点:内存管理(首次适应算法)、逻辑处理
- 时间限制:1秒
- 空间限制:256MB
- 限定语言:不限
题目描述
请实现一个简易内存池,根据请求命令完成内存分配和释放。
内存池支持两种操作命令:REQUEST
和RELEASE
,其格式为:
REQUEST=请求的内存大小
:请求分配指定大小的连续内存。- 若分配成功,返回分配到的内存首地址;
- 若内存不足或请求大小为0,输出
error
。
RELEASE=释放的内存首地址
:释放指定首地址的内存块。- 释放成功无需输出;
- 若释放不存在的首地址,输出
error
。
约束条件:
- 内存池总大小为 100字节,地址从0开始分配。
- 分配必须为连续内存,优先从低地址分配。
- 释放后的内存可被再次分配,但已释放的内存块不可二次释放。
- 不会释放已分配内存块的中间地址,仅能通过首地址操作。
输入描述:
- 首行为整数 N(操作命令数,0 < N ≤ 100)。
- 后续 N 行,每行一个操作命令,格式为
命令=参数
(如REQUEST=10
)。
输出描述:
- 按顺序输出每个
REQUEST
的分配结果或error
,RELEASE
仅需在失败时输出error
。
示例:
输入:
5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
输出:
0
10
30
0
说明:
- 第一条
REQUEST=10
分配地址0~9,返回首地址0。 - 第二条
REQUEST=20
分配地址10~29,返回首地址10。 RELEASE=0
释放首地址0的内存块,0~9变为空闲。- 第三条
REQUEST=20
因0~9空间不足,从30开始分配,返回首地址30。 - 第四条
REQUEST=10
重新分配0~9,返回首地址0。
补充说明:
- 输入保证合法,无需处理非法参数(如负数)。
- 同一列表元素顺序不可变,动态规划或回溯法为典型解法。
Java
问题分析
我们需要实现一个简易内存池,支持 REQUEST
和 RELEASE
命令。内存池总大小为 100 字节,地址从 0 开始。分配时采用首次适应算法,优先低地址分配连续内存。释放时需处理合并相邻空闲块。
解题思路
-
数据结构设计:
- 空闲块列表:按起始地址升序排列,记录每个空闲块的起始地址和大小。
- 已分配块字典:记录已分配块的首地址和大小,用于快速查找。
-
REQUEST 处理:
- 遍历空闲块列表,找到第一个足够大的块。
- 分割该块,记录已分配块,更新空闲列表。
-
RELEASE 处理:
- 检查要释放的块是否存在。
- 将释放的块插入空闲列表并合并相邻块。
代码实现
import java.util.*;class Block {int start;int size;public Block(int s, int sz) {start = s;size = sz;}
}public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int N = Integer.parseInt(scanner.nextLine());List<Block> freeList = new ArrayList<>();freeList.add(new Block(0, 100)); // 初始空闲块:0-99Map<Integer, Integer> allocated = new HashMap<>();for (int i = 0; i < N; i++) {String line = scanner.nextLine();String[] parts = line.split("=");String cmd = parts[0];int param = Integer.parseInt(parts[1]);if (cmd.equals("REQUEST")) {// REQUEST 处理int size = param;if (size <= 0) {System.out.println("error");continue;}boolean found = false;for (int j = 0; j < freeList.size(); j++) {Block block = freeList.get(j);if (block.size >= size) {// 分配该块allocated.put(block.start, size);System.out.println(block.start);// 处理剩余空间int remaining = block.size - size;if (remaining > 0) {freeList.set(j, new Block(block.start + size, remaining));} else {freeList.remove(j);}found = true;break;}}if (!found) {System.out.println("error");}} else if (cmd.equals("RELEASE")) {// RELEASE 处理int addr = param;if (!allocated.containsKey(addr)) {System.out.println("error");continue;}int size = allocated.get(addr);Block newBlock = new Block(addr, size);// 插入到正确位置int insertIdx = 0;while (insertIdx < freeList.size() && freeList.get(insertIdx).start < newBlock.start) {insertIdx++;}freeList.add(insertIdx, newBlock);// 合并左边while (insertIdx > 0) {Block prev = freeList.get(insertIdx - 1);if (prev.start + prev.size == newBlock.start) {newBlock.start = prev.start;newBlock.size += prev.size;freeList.remove(insertIdx - 1);insertIdx--;} else {break;}}// 合并右边while (insertIdx < freeList.size() - 1) {Block next = freeList.get(insertIdx + 1);if (newBlock.start + newBlock.size == next.start) {newBlock.size += next.size;freeList.remove(insertIdx + 1);} else {break;}}allocated.remove(addr);}}}
}
代码详细解析
- Block 类:表示内存块,包含起始地址
start
和大小size
。 - 初始化:
freeList
初始为0-99
的块。allocated
记录已分配块的首地址和大小。
- REQUEST 处理:
- 遍历
freeList
找第一个足够大的块。 - 分配后分割剩余空间,更新
freeList
和allocated
。
- 遍历
- RELEASE 处理:
- 检查地址是否存在,不存在则输出错误。
- 插入释放的块到
freeList
,合并相邻块。
示例测试
示例1输入:
5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
输出:
0
10
30
0
解析:
- 前两次分配分别获得 0-9 和 10-29。
- 释放 0-9 后,第三次分配 30-49。
- 第四次分配重新使用 0-9。
示例2输入:
3
REQUEST=50
RELEASE=0
REQUEST=50
输出:
0
error
0
解析:
- 首次分配 0-49,释放后合并为 0-99。
- 再次分配成功。
示例3输入:
4
REQUEST=100
RELEASE=0
REQUEST=99
REQUEST=1
输出:
0
99
error
解析:
- 首次分配全部内存,释放后再次分配 99 字节成功,剩余 1 字节不足以分配。
综合分析
-
时间复杂度:O(N^2)
- 每个
REQUEST
和RELEASE
最坏需遍历列表,但 N ≤ 100,实际可行。
- 每个
-
空间复杂度:O(N)
- 空闲块和已分配块的数量与操作数相关,但总量较小。
-
优势:
- 首次适应算法:简单高效,优先低地址分配。
- 合并策略:释放时合并相邻块,减少内存碎片。
-
适用场景:
- 适用于操作数较少且内存管理简单的场景。
python
问题分析
我们需要实现一个简易内存池,支持 REQUEST
和 RELEASE
操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先从低地址分配连续内存。释放后需合并相邻空闲块。
解题思路
-
数据结构设计:
- 空闲块列表:按起始地址升序排列,每个块记录起始地址和大小。
- 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
-
REQUEST 处理:
- 遍历空闲块列表,找到第一个足够大的块。
- 分割该块,记录已分配块,更新空闲列表。
-
RELEASE 处理:
- 检查要释放的块是否存在。
- 将释放的块插入空闲列表并合并相邻块。
代码实现
import bisectn = int(input())
free_blocks = [(0, 100)] # 空闲块列表,按起始地址排序
allocated = {} # 已分配块字典 {起始地址: 大小}for _ in range(n):line = input().strip()cmd, param = line.split('=')param = int(param)if cmd == 'REQUEST':size = paramif size <= 0:print('error')continuefound = False# 遍历空闲块,找到第一个足够大的块for i in range(len(free_blocks)):start, block_size = free_blocks[i]if block_size >= size:print(start)allocated[start] = size# 处理剩余空间remaining = block_size - sizeif remaining > 0:free_blocks[i] = (start + size, remaining)else:del free_blocks[i]found = Truebreakif not found:print('error')elif cmd == 'RELEASE':addr = paramif addr not in allocated:print('error')continuesize = allocated[addr]del allocated[addr]# 将释放的块插入空闲列表starts = [block[0] for block in free_blocks]i = bisect.bisect_left(starts, addr)free_blocks.insert(i, (addr, size))# 合并左侧的空闲块while i > 0 and free_blocks[i-1][0] + free_blocks[i-1][1] == free_blocks[i][0]:prev_start, prev_size = free_blocks.pop(i-1)free_blocks[i-1] = (prev_start, prev_size + free_blocks[i-1][1])i -= 1# 合并右侧的空闲块while i < len(free_blocks)-1 and free_blocks[i][0] + free_blocks[i][1] == free_blocks[i+1][0]:curr_start, curr_size = free_blocks.pop(i)next_start, next_size = free_blocks.pop(i)free_blocks.insert(i, (curr_start, curr_size + next_size))
代码详细解析
-
输入处理:
- 读取操作数
n
,逐行解析命令。 free_blocks
初始化为[(0, 100)]
,表示整个内存池空闲。allocated
字典记录已分配块。
- 读取操作数
-
REQUEST 处理:
- 遍历
free_blocks
,找到首个足够大的块。 - 分配后更新
allocated
和free_blocks
,分割剩余块。
- 遍历
-
RELEASE 处理:
- 检查地址是否在
allocated
中,若不存在输出错误。 - 使用
bisect
插入释放的块到正确位置。 - 合并左侧和右侧的空闲块,确保连续空间合并。
- 检查地址是否在
示例测试
示例1输入:
5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
输出:
0
10
30
0
解析:
- 前两次分配地址0-9和10-29。
- 释放0-9后,第三次分配30-49。
- 第四次重新分配0-9。
示例2输入:
3
REQUEST=50
RELEASE=0
REQUEST=50
输出:
0
0
解析:
- 首次分配0-49,释放后合并为0-99,再次分配成功。
示例3输入:
4
REQUEST=100
RELEASE=0
REQUEST=99
REQUEST=1
输出:
0
99
error
解析:
- 分配全部内存后释放,再次分配99字节成功,剩余1字节不足。
综合分析
-
时间复杂度:O(N²)
- 每个
REQUEST
最坏遍历整个空闲列表,但操作数 ≤ 100,实际可行。
- 每个
-
空间复杂度:O(N)
- 空闲块和已分配块数量与操作数相关,内存消耗较小。
-
优势:
- 首次适应算法:快速找到可用块,保证低地址优先。
- 高效合并:释放后合并相邻块,减少碎片。
-
适用场景:
- 适用于内存分配操作较少、需要高效管理连续内存的场景。
JavaScript
问题分析
我们需要实现一个简易内存池,支持 REQUEST
和 RELEASE
操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先低地址分配连续内存。释放后需合并相邻空闲块。
解题思路
- 数据结构设计:
- 空闲块列表:按起始地址升序排列,每个块记录
start
和size
。 - 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
- 空闲块列表:按起始地址升序排列,每个块记录
- REQUEST 处理:
- 遍历空闲块列表,找到第一个足够大的块。
- 分割该块,记录已分配块,更新空闲列表。
- RELEASE 处理:
- 检查要释放的块是否存在。
- 将释放的块插入空闲列表并合并相邻块。
代码实现
const readline = require("readline");const rl = readline.createInterface({input: process.stdin,output: process.stdout,terminal: false,
});let lineCount = 0;
let n;
const freeBlocks = [{ start: 0, size: 100 }]; // 空闲块列表(按地址升序)
const allocated = new Map(); // 已分配块字典rl.on("line", (line) => {if (lineCount === 0) {n = parseInt(line.trim());} else {const [cmd, param] = line.trim().split("=");const value = parseInt(param);if (cmd === "REQUEST") {handleRequest(value);} else if (cmd === "RELEASE") {handleRelease(value);}}lineCount++;
});// 处理 REQUEST 命令
function handleRequest(size) {if (size <= 0) {console.log("error");return;}for (let i = 0; i < freeBlocks.length; i++) {const block = freeBlocks[i];if (block.size >= size) {// 分配该块console.log(block.start);allocated.set(block.start, size);// 分割剩余空间const remaining = block.size - size;if (remaining > 0) {freeBlocks.splice(i, 1, { start: block.start + size, size: remaining });} else {freeBlocks.splice(i, 1);}return;}}// 没有找到足够大的块console.log("error");
}// 处理 RELEASE 命令
function handleRelease(addr) {if (!allocated.has(addr)) {console.log("error");return;}const size = allocated.get(addr);allocated.delete(addr);// 找到插入位置let insertIndex = 0;while (insertIndex < freeBlocks.length &&freeBlocks[insertIndex].start < addr) {insertIndex++;}// 插入新空闲块const newBlock = { start: addr, size };freeBlocks.splice(insertIndex, 0, newBlock);// 合并左侧空闲块while (insertIndex > 0) {const prevBlock = freeBlocks[insertIndex - 1];if (prevBlock.start + prevBlock.size === newBlock.start) {newBlock.start = prevBlock.start;newBlock.size += prevBlock.size;freeBlocks.splice(insertIndex - 1, 1);insertIndex--;} else {break;}}// 合并右侧空闲块while (insertIndex < freeBlocks.length - 1) {const nextBlock = freeBlocks[insertIndex + 1];if (newBlock.start + newBlock.size === nextBlock.start) {newBlock.size += nextBlock.size;freeBlocks.splice(insertIndex + 1, 1);} else {break;}}
}
代码详细解析
-
输入处理:
- 使用
readline
逐行读取输入。 freeBlocks
初始化为[{ start: 0, size: 100 }]
,表示整个内存池空闲。allocated
用Map
记录已分配块。
- 使用
-
REQUEST 处理:
- 遍历
freeBlocks
寻找足够大的块。 - 输出分配的首地址,并更新空闲列表:
freeBlocks.splice(i, 1, { start: block.start + size, size: remaining });
- 遍历
-
RELEASE 处理:
- 检查地址是否存在,不存在则输出错误。
- 找到插入位置并插入释放的块:
while (insertIndex < freeBlocks.length && freeBlocks[insertIndex].start < addr) {insertIndex++; }
- 合并左侧和右侧相邻块,确保连续空间合并。
示例测试
示例1输入:
5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
输出:
0
10
30
0
解析:
- 前两次分配地址
0-9
和10-29
。 - 释放后合并空闲块,第三次分配
30-49
,第四次重新分配0-9
。
示例2输入:
3
REQUEST=50
RELEASE=0
REQUEST=50
输出:
0
0
解析:
- 首次分配
0-49
,释放后合并为0-99
,再次分配成功。
示例3输入:
4
REQUEST=100
RELEASE=0
REQUEST=99
REQUEST=1
输出:
0
99
error
解析:
- 分配全部内存后释放,再次分配
99
字节成功,剩余1
字节不足。
综合分析
-
时间复杂度:
- REQUEST:最坏 O(N),需遍历空闲列表。
- RELEASE:最坏 O(N),需遍历插入位置并合并相邻块。
-
空间复杂度:
- 空闲块和已分配块数量与操作数相关,内存消耗较小。
-
优势:
- 首次适应算法:快速找到可用块,保证低地址优先。
- 高效合并:释放后自动合并相邻块,减少内存碎片。
-
适用场景:
- 适用于内存分配操作较少、需要高效管理连续内存的场景。
C++
问题分析
我们需要实现一个简易内存池,支持 REQUEST
和 RELEASE
操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先低地址分配连续内存。释放后的内存需合并相邻空闲块。
解题思路
-
数据结构设计:
- 空闲块列表:按起始地址升序排列,记录每个空闲块的起始地址和大小。
- 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
-
REQUEST 处理:
- 遍历空闲块列表,找到第一个足够大的块。
- 分割该块,记录已分配块,更新空闲列表。
-
RELEASE 处理:
- 检查要释放的块是否存在。
- 将释放的块插入空闲列表并合并相邻块。
代码实现
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>using namespace std;int main() {int n;cin >> n;vector<pair<int, int>> free_blocks; // 空闲块列表(按起始地址升序)free_blocks.emplace_back(0, 100); // 初始内存池:0~99unordered_map<int, int> allocated; // 已分配块字典 {起始地址: 大小}for (int i = 0; i < n; ++i) {string line;cin >> line;size_t eq_pos = line.find('=');string cmd = line.substr(0, eq_pos);int param = stoi(line.substr(eq_pos + 1));if (cmd == "REQUEST") {int size = param;if (size <= 0) {cout << "error" << endl;continue;}bool found = false;// 遍历空闲块列表,找到第一个足够大的块for (auto it = free_blocks.begin(); it != free_blocks.end();) {int start = it->first;int block_size = it->second;if (block_size >= size) {// 分配该块cout << start << endl;allocated[start] = size;// 处理剩余空间int remaining = block_size - size;if (remaining > 0) {*it = {start + size, remaining}; // 修改当前块为剩余部分} else {free_blocks.erase(it); // 无剩余则删除该块}found = true;break;} else {++it; // 继续查找下一个块}}if (!found) {cout << "error" << endl;}} else if (cmd == "RELEASE") {int addr = param;auto it = allocated.find(addr);if (it == allocated.end()) {cout << "error" << endl;continue;}int size = it->second;allocated.erase(it); // 从已分配字典中删除// 将释放的块插入到空闲列表的正确位置pair<int, int> new_block = {addr, size};auto insert_it = lower_bound(free_blocks.begin(), free_blocks.end(), new_block,[](const pair<int, int>& a, const pair<int, int>& b) {return a.first < b.first;});int pos = insert_it - free_blocks.begin();free_blocks.insert(insert_it, new_block);// 合并左侧相邻块while (pos > 0 && free_blocks[pos - 1].first + free_blocks[pos - 1].second == free_blocks[pos].first) {free_blocks[pos - 1].second += free_blocks[pos].second;free_blocks.erase(free_blocks.begin() + pos);pos--;}// 合并右侧相邻块while (pos < free_blocks.size() - 1 && free_blocks[pos].first + free_blocks[pos].second == free_blocks[pos + 1].first) {free_blocks[pos].second += free_blocks[pos + 1].second;free_blocks.erase(free_blocks.begin() + pos + 1);}}}return 0;
}
代码详细解析
-
数据结构初始化:
free_blocks
初始化为{0, 100}
,表示整个内存池空闲。allocated
用哈希表记录已分配块。
-
REQUEST 处理:
- 遍历
free_blocks
,找到首个足够大的块。 - 分割该块:若剩余空间大于0,修改当前块;否则删除该块。
- 输出分配的首地址,并记录到
allocated
。
- 遍历
-
RELEASE 处理:
- 检查地址是否存在,不存在则输出错误。
- 使用
lower_bound
找到插入位置,保持空闲块有序。 - 插入后合并左右相邻的空闲块,确保内存连续。
示例测试
示例1输入:
5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
输出:
0
10
30
0
解析:
- 分配
0-9
和10-29
,释放后合并空闲块,第三次分配30-49
,第四次分配0-9
。
示例2输入:
3
REQUEST=50
RELEASE=0
REQUEST=50
输出:
0
0
解析:
- 首次分配
0-49
,释放后合并为0-99
,再次分配成功。
示例3输入:
4
REQUEST=100
RELEASE=0
REQUEST=99
REQUEST=1
输出:
0
99
error
解析:
- 分配全部内存后释放,再次分配
99
字节成功,剩余1字节不足。
综合分析
-
时间复杂度:
- REQUEST:最坏 O(N),需遍历空闲块列表。
- RELEASE:插入和合并操作最坏 O(N),但操作数 ≤ 100,实际高效。
-
空间复杂度:
- 空闲块和已分配块数量与操作数相关,内存占用较小。
-
优势:
- 首次适应算法:优先低地址分配,减少内存碎片。
- 高效合并:释放时自动合并相邻块,优化内存利用率。
-
适用场景:
- 适用于内存操作较少、需高效管理连续内存的场景。
C语言
问题分析
我们需要实现一个简易内存池,支持 REQUEST
和 RELEASE
操作。内存池总大小为 100 字节,地址从 0 开始。分配时采用首次适应算法,优先从低地址分配连续内存。释放时需合并相邻空闲块。
解题思路
- 数据结构设计:
- 空闲块数组:按起始地址升序排列,记录每个空闲块的起始地址和大小。
- 已分配块数组:记录已分配块的首地址和大小,便于快速查找。
- REQUEST 处理:
- 遍历空闲块数组,找到第一个足够大的块。
- 分割该块,记录已分配块,更新空闲数组。
- RELEASE 处理:
- 检查要释放的块是否存在,不存在则输出错误。
- 将释放的块插入空闲数组,合并相邻块。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct {int start;int size;
} Block;Block *free_blocks = NULL; // 空闲块数组
int free_count = 0; // 空闲块数量typedef struct {int start;int size;
} AllocatedBlock;AllocatedBlock *allocated = NULL; // 已分配块数组
int allocated_count = 0; // 已分配块数量// 初始化内存池,初始时整个内存池空闲
void init_free_blocks() {free_blocks = malloc(sizeof(Block));free_blocks[0].start = 0;free_blocks[0].size = 100;free_count = 1;
}// 处理 REQUEST 命令
void handle_request(int size) {if (size <= 0) {printf("error\n");return;}// 遍历空闲块数组,找到第一个足够大的块for (int i = 0; i < free_count; i++) {if (free_blocks[i].size >= size) {int allocated_start = free_blocks[i].start;printf("%d\n", allocated_start);// 记录到已分配数组AllocatedBlock *temp = realloc(allocated, (allocated_count + 1) * sizeof(AllocatedBlock));if (!temp) {fprintf(stderr, "Memory allocation failed\n");exit(1);}allocated = temp;allocated[allocated_count].start = allocated_start;allocated[allocated_count].size = size;allocated_count++;// 处理剩余空间int remaining = free_blocks[i].size - size;if (remaining > 0) {// 修改当前空闲块的起始地址和大小free_blocks[i].start += size;free_blocks[i].size = remaining;} else {// 无剩余空间,移除该空闲块memmove(&free_blocks[i], &free_blocks[i + 1], (free_count - i - 1) * sizeof(Block));free_count--;free_blocks = realloc(free_blocks, free_count * sizeof(Block));}return;}}// 无足够大的空闲块printf("error\n");
}// 处理 RELEASE 命令
void handle_release(int addr) {// 查找要释放的块是否存在int found = 0;int size = 0;int index = -1;for (int i = 0; i < allocated_count; i++) {if (allocated[i].start == addr) {found = 1;size = allocated[i].size;index = i;break;}}if (!found) {printf("error\n");return;}// 从已分配数组中移除memmove(&allocated[index], &allocated[index + 1], (allocated_count - index - 1) * sizeof(AllocatedBlock));allocated_count--;allocated = realloc(allocated, allocated_count * sizeof(AllocatedBlock));// 创建新空闲块Block new_block = {addr, size};// 找到插入位置(按地址升序)int insert_pos = 0;while (insert_pos < free_count && free_blocks[insert_pos].start < new_block.start) {insert_pos++;}// 插入到空闲数组free_blocks = realloc(free_blocks, (free_count + 1) * sizeof(Block));memmove(&free_blocks[insert_pos + 1], &free_blocks[insert_pos], (free_count - insert_pos) * sizeof(Block));free_blocks[insert_pos] = new_block;free_count++;// 合并左侧相邻块while (insert_pos > 0) {Block prev = free_blocks[insert_pos - 1];if (prev.start + prev.size == new_block.start) {free_blocks[insert_pos - 1].size += new_block.size;memmove(&free_blocks[insert_pos], &free_blocks[insert_pos + 1], (free_count - insert_pos - 1) * sizeof(Block));free_count--;insert_pos--;new_block = free_blocks[insert_pos];} else {break;}}// 合并右侧相邻块while (insert_pos < free_count - 1) {Block next = free_blocks[insert_pos + 1];if (new_block.start + new_block.size == next.start) {free_blocks[insert_pos].size += next.size;memmove(&free_blocks[insert_pos + 1], &free_blocks[insert_pos + 2], (free_count - insert_pos - 2) * sizeof(Block));free_count--;} else {break;}}
}int main() {init_free_blocks();int n;scanf("%d", &n);getchar(); // 读取换行符for (int i = 0; i < n; i++) {char line[50];fgets(line, sizeof(line), stdin);line[strcspn(line, "\n")] = '\0'; // 去除换行符char *eq = strchr(line, '=');if (!eq) continue; // 输入格式错误,跳过*eq = '\0';char *cmd = line;int param = atoi(eq + 1);if (strcmp(cmd, "REQUEST") == 0) {handle_request(param);} else if (strcmp(cmd, "RELEASE") == 0) {handle_release(param);}}free(free_blocks);free(allocated);return 0;
}
代码详细解析
-
数据结构:
Block
结构体表示空闲块,包含起始地址和大小。AllocatedBlock
结构体表示已分配块。free_blocks
和allocated
是动态数组,分别存储空闲块和已分配块。
-
初始化:
init_free_blocks
初始化内存池,初始空闲块为0-99
。
-
REQUEST 处理:
- 遍历
free_blocks
数组,找到第一个足够大的块。 - 分割该块,剩余空间更新到原位置或删除原块。
- 记录已分配块到
allocated
数组。
- 遍历
-
RELEASE 处理:
- 查找要释放的块是否存在。
- 插入到
free_blocks
数组的正确位置。 - 合并左侧和右侧相邻的空闲块。
-
输入处理:
- 读取命令并解析参数,调用对应处理函数。
示例测试
示例1输入:
5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
输出:
0
10
30
0
解析:
- 分配
0-9
和10-29
,释放0-9
后合并空闲块,重新分配0-9
。
示例2输入:
3
REQUEST=50
RELEASE=0
REQUEST=50
输出:
0
0
解析:
- 首次分配
0-49
,释放后合并为0-99
,再次分配成功。
示例3输入:
4
REQUEST=100
RELEASE=0
REQUEST=99
REQUEST=1
输出:
0
99
error
解析:
- 分配全部内存后释放,再次分配
99
字节成功,剩余1
字节不足。
综合分析
-
时间复杂度:
- REQUEST:O(N),遍历空闲数组。
- RELEASE:O(N),查找和合并操作。
-
空间复杂度:
- 动态数组存储空闲块和已分配块,内存占用可控。
-
优势:
- 首次适应算法:优先低地址分配,减少内存碎片。
- 高效合并:释放后自动合并相邻块,优化内存利用率。
-
适用场景:
- 适用于操作数较少、需高效管理连续内存的场景。
GO
问题分析
我们需要实现一个简易内存池,支持 REQUEST
和 RELEASE
操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先低地址分配连续内存。释放后需合并相邻空闲块。
解题思路
-
数据结构设计:
- 空闲块列表:按起始地址升序排列,记录每个空闲块的起始地址和大小。
- 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
-
REQUEST 处理:
- 遍历空闲块列表,找到第一个足够大的块。
- 分割该块,记录已分配块,更新空闲列表。
-
RELEASE 处理:
- 检查要释放的块是否存在。
- 将释放的块插入空闲列表并合并相邻块。
代码实现
package mainimport ("bufio""fmt""os""strconv""strings"
)type Block struct {Start intSize int
}func main() {scanner := bufio.NewScanner(os.Stdin)// 初始化内存池:0~99 空闲freeBlocks := []Block{{Start: 0, Size: 100}}allocated := make(map[int]int) // 已分配块:key=起始地址,value=大小scanner.Scan()n, _ := strconv.Atoi(scanner.Text())for i := 0; i < n; i++ {scanner.Scan()line := scanner.Text()parts := strings.Split(line, "=")cmd := parts[0]param, _ := strconv.Atoi(parts[1])if cmd == "REQUEST" {size := paramif size <= 0 {fmt.Println("error")continue}found := false// 遍历空闲块,找到第一个足够大的块for idx, block := range freeBlocks {if block.Size >= size {// 分配该块fmt.Println(block.Start)allocated[block.Start] = size// 处理剩余空间remaining := block.Size - sizeif remaining > 0 {// 修改当前空闲块freeBlocks[idx] = Block{Start: block.Start + size,Size: remaining,}} else {// 移除该空闲块freeBlocks = append(freeBlocks[:idx], freeBlocks[idx+1:]...)}found = truebreak}}if !found {fmt.Println("error")}} else if cmd == "RELEASE" {addr := paramsize, exists := allocated[addr]if !exists {fmt.Println("error")continue}// 从已分配字典中删除delete(allocated, addr)// 创建新空闲块newBlock := Block{Start: addr, Size: size}// 找到插入位置(保持按起始地址升序)insertIdx := 0for insertIdx < len(freeBlocks) && freeBlocks[insertIdx].Start < newBlock.Start {insertIdx++}// 插入新块freeBlocks = append(freeBlocks[:insertIdx], append([]Block{newBlock}, freeBlocks[insertIdx:]...)...)// 合并左侧相邻块for insertIdx > 0 {left := freeBlocks[insertIdx-1]if left.Start+left.Size == newBlock.Start {newBlock.Start = left.StartnewBlock.Size += left.Size// 移除左侧块freeBlocks = append(freeBlocks[:insertIdx-1], freeBlocks[insertIdx:]...)insertIdx--} else {break}}// 合并右侧相邻块for insertIdx < len(freeBlocks)-1 {right := freeBlocks[insertIdx+1]if newBlock.Start+newBlock.Size == right.Start {newBlock.Size += right.Size// 移除右侧块freeBlocks = append(freeBlocks[:insertIdx+1], freeBlocks[insertIdx+2:]...)} else {break}}// 更新合并后的块freeBlocks[insertIdx] = newBlock}}
}
代码详细解析
-
数据结构定义:
type Block struct {Start intSize int }
Block
结构体表示空闲块,包含起始地址和大小。
-
初始化:
freeBlocks := []Block{{Start: 0, Size: 100}} allocated := make(map[int]int)
freeBlocks
初始化为0~99
的空闲块。allocated
字典记录已分配块的起始地址和大小。
-
输入处理:
scanner.Scan() n, _ := strconv.Atoi(scanner.Text())
- 读取命令数量
n
,逐行处理每个命令。
- 读取命令数量
-
REQUEST 处理:
- 遍历
freeBlocks
查找第一个足够大的块:for idx, block := range freeBlocks {if block.Size >= size {// 分配该块fmt.Println(block.Start)allocated[block.Start] = size
- 分割剩余空间或移除空闲块:
remaining := block.Size - size if remaining > 0 {freeBlocks[idx] = Block{Start: block.Start + size, Size: remaining} } else {freeBlocks = append(freeBlocks[:idx], freeBlocks[idx+1:]...) }
- 遍历
-
RELEASE 处理:
- 检查地址是否存在:
size, exists := allocated[addr] if !exists {fmt.Println("error")continue }
- 插入新空闲块到正确位置:
insertIdx := 0 for insertIdx < len(freeBlocks) && freeBlocks[insertIdx].Start < newBlock.Start {insertIdx++ } freeBlocks = append(freeBlocks[:insertIdx], append([]Block{newBlock}, freeBlocks[insertIdx:]...)...)
- 合并左侧和右侧相邻块:
for insertIdx > 0 {left := freeBlocks[insertIdx-1]if left.Start+left.Size == newBlock.Start {newBlock.Start = left.StartnewBlock.Size += left.SizefreeBlocks = append(freeBlocks[:insertIdx-1], freeBlocks[insertIdx:]...)insertIdx--} }
- 检查地址是否存在:
示例测试
示例1输入:
5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
输出:
0
10
30
0
解析:
- 分配
0~9
和10~29
,释放后合并空闲块,第三次分配30~49
,第四次分配0~9
。
示例2输入:
3
REQUEST=50
RELEASE=0
REQUEST=50
输出:
0
0
解析:
- 首次分配
0~49
,释放后合并为0~99
,再次分配成功。
示例3输入:
4
REQUEST=100
RELEASE=0
REQUEST=99
REQUEST=1
输出:
0
99
error
解析:
- 分配全部内存后释放,再次分配
99
字节成功,剩余1
字节不足。
综合分析
-
时间复杂度:
- REQUEST:O(N),遍历空闲块列表。
- RELEASE:O(N),插入和合并操作。
-
空间复杂度:
- 空闲块和已分配块的数量与操作数相关,内存占用可控。
-
优势:
- 首次适应算法:优先低地址分配,减少内存碎片。
- 高效合并:释放后自动合并相邻块,优化内存利用率。
-
适用场景:
- 适用于内存操作较少、需要高效管理连续内存的场景。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!