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(N2)
- 每個
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夢】,關注我,獲取更多實用教程/資源!