华为OD机试真题——简易内存池(2025A卷:200分)Java/python/JavaScript/C++/C/GO最佳实现

在这里插入图片描述

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
  • 限定语言:不限

题目描述

请实现一个简易内存池,根据请求命令完成内存分配和释放。
内存池支持两种操作命令:REQUESTRELEASE,其格式为:

  1. REQUEST=请求的内存大小:请求分配指定大小的连续内存。
    • 若分配成功,返回分配到的内存首地址;
    • 若内存不足或请求大小为0,输出 error
  2. RELEASE=释放的内存首地址:释放指定首地址的内存块。
    • 释放成功无需输出;
    • 若释放不存在的首地址,输出 error

约束条件

  1. 内存池总大小为 100字节,地址从0开始分配。
  2. 分配必须为连续内存,优先从低地址分配。
  3. 释放后的内存可被再次分配,但已释放的内存块不可二次释放。
  4. 不会释放已分配内存块的中间地址,仅能通过首地址操作。

输入描述

  • 首行为整数 N(操作命令数,0 < N ≤ 100)。
  • 后续 N 行,每行一个操作命令,格式为 命令=参数(如 REQUEST=10)。

输出描述

  • 按顺序输出每个 REQUEST 的分配结果或 errorRELEASE 仅需在失败时输出 error

示例
输入:

5  
REQUEST=10  
REQUEST=20  
RELEASE=0  
REQUEST=20  
REQUEST=10  

输出:

0  
10  
30  
0  

说明

  1. 第一条 REQUEST=10 分配地址0~9,返回首地址0。
  2. 第二条 REQUEST=20 分配地址10~29,返回首地址10。
  3. RELEASE=0 释放首地址0的内存块,0~9变为空闲。
  4. 第三条 REQUEST=20 因0~9空间不足,从30开始分配,返回首地址30。
  5. 第四条 REQUEST=10 重新分配0~9,返回首地址0。

补充说明

  • 输入保证合法,无需处理非法参数(如负数)。
  • 同一列表元素顺序不可变,动态规划或回溯法为典型解法。

Java

问题分析

我们需要实现一个简易内存池,支持 REQUESTRELEASE 命令。内存池总大小为 100 字节,地址从 0 开始。分配时采用首次适应算法,优先低地址分配连续内存。释放时需处理合并相邻空闲块。


解题思路

  1. 数据结构设计

    • 空闲块列表:按起始地址升序排列,记录每个空闲块的起始地址和大小。
    • 已分配块字典:记录已分配块的首地址和大小,用于快速查找。
  2. REQUEST 处理

    • 遍历空闲块列表,找到第一个足够大的块。
    • 分割该块,记录已分配块,更新空闲列表。
  3. 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);}}}
}

代码详细解析

  1. Block 类:表示内存块,包含起始地址 start 和大小 size
  2. 初始化
    • freeList 初始为 0-99 的块。
    • allocated 记录已分配块的首地址和大小。
  3. REQUEST 处理
    • 遍历 freeList 找第一个足够大的块。
    • 分配后分割剩余空间,更新 freeListallocated
  4. 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 字节不足以分配。

综合分析

  1. 时间复杂度:O(N^2)

    • 每个 REQUESTRELEASE 最坏需遍历列表,但 N ≤ 100,实际可行。
  2. 空间复杂度:O(N)

    • 空闲块和已分配块的数量与操作数相关,但总量较小。
  3. 优势

    • 首次适应算法:简单高效,优先低地址分配。
    • 合并策略:释放时合并相邻块,减少内存碎片。
  4. 适用场景

    • 适用于操作数较少且内存管理简单的场景。

python

问题分析

我们需要实现一个简易内存池,支持 REQUESTRELEASE 操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先从低地址分配连续内存。释放后需合并相邻空闲块。


解题思路

  1. 数据结构设计

    • 空闲块列表:按起始地址升序排列,每个块记录起始地址和大小。
    • 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
  2. REQUEST 处理

    • 遍历空闲块列表,找到第一个足够大的块。
    • 分割该块,记录已分配块,更新空闲列表。
  3. 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))

代码详细解析

  1. 输入处理

    • 读取操作数 n,逐行解析命令。
    • free_blocks 初始化为 [(0, 100)],表示整个内存池空闲。
    • allocated 字典记录已分配块。
  2. REQUEST 处理

    • 遍历 free_blocks,找到首个足够大的块。
    • 分配后更新 allocatedfree_blocks,分割剩余块。
  3. 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字节不足。

综合分析

  1. 时间复杂度:O(N²)

    • 每个 REQUEST 最坏遍历整个空闲列表,但操作数 ≤ 100,实际可行。
  2. 空间复杂度:O(N)

    • 空闲块和已分配块数量与操作数相关,内存消耗较小。
  3. 优势

    • 首次适应算法:快速找到可用块,保证低地址优先。
    • 高效合并:释放后合并相邻块,减少碎片。
  4. 适用场景

    • 适用于内存分配操作较少、需要高效管理连续内存的场景。

JavaScript

问题分析

我们需要实现一个简易内存池,支持 REQUESTRELEASE 操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先低地址分配连续内存。释放后需合并相邻空闲块。


解题思路

  1. 数据结构设计
    • 空闲块列表:按起始地址升序排列,每个块记录 startsize
    • 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
  2. REQUEST 处理
    • 遍历空闲块列表,找到第一个足够大的块。
    • 分割该块,记录已分配块,更新空闲列表。
  3. 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;}}
}

代码详细解析

  1. 输入处理

    • 使用 readline 逐行读取输入。
    • freeBlocks 初始化为 [{ start: 0, size: 100 }],表示整个内存池空闲。
    • allocatedMap 记录已分配块。
  2. REQUEST 处理

    • 遍历 freeBlocks 寻找足够大的块。
    • 输出分配的首地址,并更新空闲列表:
      freeBlocks.splice(i, 1, { start: block.start + size, size: remaining });
      
  3. 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-910-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 字节不足。

综合分析

  1. 时间复杂度

    • REQUEST:最坏 O(N),需遍历空闲列表。
    • RELEASE:最坏 O(N),需遍历插入位置并合并相邻块。
  2. 空间复杂度

    • 空闲块和已分配块数量与操作数相关,内存消耗较小。
  3. 优势

    • 首次适应算法:快速找到可用块,保证低地址优先。
    • 高效合并:释放后自动合并相邻块,减少内存碎片。
  4. 适用场景

    • 适用于内存分配操作较少、需要高效管理连续内存的场景。

C++

问题分析

我们需要实现一个简易内存池,支持 REQUESTRELEASE 操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先低地址分配连续内存。释放后的内存需合并相邻空闲块。


解题思路

  1. 数据结构设计

    • 空闲块列表:按起始地址升序排列,记录每个空闲块的起始地址和大小。
    • 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
  2. REQUEST 处理

    • 遍历空闲块列表,找到第一个足够大的块。
    • 分割该块,记录已分配块,更新空闲列表。
  3. 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;
}

代码详细解析

  1. 数据结构初始化

    • free_blocks 初始化为 {0, 100},表示整个内存池空闲。
    • allocated 用哈希表记录已分配块。
  2. REQUEST 处理

    • 遍历 free_blocks,找到首个足够大的块。
    • 分割该块:若剩余空间大于0,修改当前块;否则删除该块。
    • 输出分配的首地址,并记录到 allocated
  3. RELEASE 处理

    • 检查地址是否存在,不存在则输出错误。
    • 使用 lower_bound 找到插入位置,保持空闲块有序。
    • 插入后合并左右相邻的空闲块,确保内存连续。

示例测试

示例1输入:
5  
REQUEST=10  
REQUEST=20  
RELEASE=0  
REQUEST=20  
REQUEST=10  

输出

0  
10  
30  
0  

解析

  • 分配 0-910-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字节不足。

综合分析

  1. 时间复杂度

    • REQUEST:最坏 O(N),需遍历空闲块列表。
    • RELEASE:插入和合并操作最坏 O(N),但操作数 ≤ 100,实际高效。
  2. 空间复杂度

    • 空闲块和已分配块数量与操作数相关,内存占用较小。
  3. 优势

    • 首次适应算法:优先低地址分配,减少内存碎片。
    • 高效合并:释放时自动合并相邻块,优化内存利用率。
  4. 适用场景

    • 适用于内存操作较少、需高效管理连续内存的场景。

C语言

问题分析

我们需要实现一个简易内存池,支持 REQUESTRELEASE 操作。内存池总大小为 100 字节,地址从 0 开始。分配时采用首次适应算法,优先从低地址分配连续内存。释放时需合并相邻空闲块。


解题思路

  1. 数据结构设计
    • 空闲块数组:按起始地址升序排列,记录每个空闲块的起始地址和大小。
    • 已分配块数组:记录已分配块的首地址和大小,便于快速查找。
  2. REQUEST 处理
    • 遍历空闲块数组,找到第一个足够大的块。
    • 分割该块,记录已分配块,更新空闲数组。
  3. 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;
}

代码详细解析

  1. 数据结构

    • Block 结构体表示空闲块,包含起始地址和大小。
    • AllocatedBlock 结构体表示已分配块。
    • free_blocksallocated 是动态数组,分别存储空闲块和已分配块。
  2. 初始化

    • init_free_blocks 初始化内存池,初始空闲块为 0-99
  3. REQUEST 处理

    • 遍历 free_blocks 数组,找到第一个足够大的块。
    • 分割该块,剩余空间更新到原位置或删除原块。
    • 记录已分配块到 allocated 数组。
  4. RELEASE 处理

    • 查找要释放的块是否存在。
    • 插入到 free_blocks 数组的正确位置。
    • 合并左侧和右侧相邻的空闲块。
  5. 输入处理

    • 读取命令并解析参数,调用对应处理函数。

示例测试

示例1输入:
5  
REQUEST=10  
REQUEST=20  
RELEASE=0  
REQUEST=20  
REQUEST=10  

输出

0  
10  
30  
0  

解析

  • 分配 0-910-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 字节不足。

综合分析

  1. 时间复杂度

    • REQUEST:O(N),遍历空闲数组。
    • RELEASE:O(N),查找和合并操作。
  2. 空间复杂度

    • 动态数组存储空闲块和已分配块,内存占用可控。
  3. 优势

    • 首次适应算法:优先低地址分配,减少内存碎片。
    • 高效合并:释放后自动合并相邻块,优化内存利用率。
  4. 适用场景

    • 适用于操作数较少、需高效管理连续内存的场景。

GO

问题分析

我们需要实现一个简易内存池,支持 REQUESTRELEASE 操作。内存池总大小为 100 字节,地址从 0 开始分配。分配时采用首次适应算法,优先低地址分配连续内存。释放后需合并相邻空闲块。


解题思路

  1. 数据结构设计

    • 空闲块列表:按起始地址升序排列,记录每个空闲块的起始地址和大小。
    • 已分配块字典:记录已分配块的首地址和大小,便于快速查找。
  2. REQUEST 处理

    • 遍历空闲块列表,找到第一个足够大的块。
    • 分割该块,记录已分配块,更新空闲列表。
  3. 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}}
}

代码详细解析

  1. 数据结构定义

    type Block struct {Start intSize  int
    }
    
    • Block 结构体表示空闲块,包含起始地址和大小。
  2. 初始化

    freeBlocks := []Block{{Start: 0, Size: 100}}
    allocated := make(map[int]int)
    
    • freeBlocks 初始化为 0~99 的空闲块。
    • allocated 字典记录已分配块的起始地址和大小。
  3. 输入处理

    scanner.Scan()
    n, _ := strconv.Atoi(scanner.Text())
    
    • 读取命令数量 n,逐行处理每个命令。
  4. 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:]...)
      }
      
  5. 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~910~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 字节不足。

综合分析

  1. 时间复杂度

    • REQUEST:O(N),遍历空闲块列表。
    • RELEASE:O(N),插入和合并操作。
  2. 空间复杂度

    • 空闲块和已分配块的数量与操作数相关,内存占用可控。
  3. 优势

    • 首次适应算法:优先低地址分配,减少内存碎片。
    • 高效合并:释放后自动合并相邻块,优化内存利用率。
  4. 适用场景

    • 适用于内存操作较少、需要高效管理连续内存的场景。

更多内容:

https://www.kdocs.cn/l/cvk0eoGYucWA

本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78457.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯一面面经:总结一下

1. Java 中的 和 equals 有什么区别&#xff1f;比较对象时使用哪一个 1. 操作符&#xff1a; 用于比较对象的内存地址&#xff08;引用是否相同&#xff09;。 对于基本数据类型、 比较的是值。&#xff08;8种基本数据类型&#xff09;对于引用数据类型、 比较的是两个引…

计算机网络中的DHCP是什么呀? 详情解答

目录 DHCP 是什么&#xff1f; DHCP 的工作原理 主要功能 DHCP 与网络安全的关系 1. 正面作用 2. 潜在安全风险 DHCP 的已知漏洞 1. 协议设计缺陷 2. 软件实现漏洞 3. 配置错误导致的漏洞 4. 已知漏洞总结 举例说明 DHCP 与网络安全 如何提升 DHCP 安全性 总结 D…

2025 年导游证报考条件新政策解读与应对策略

2025 年导游证报考政策有了不少新变化&#xff0c;这些变化会对报考者产生哪些影响&#xff1f;我们又该如何应对&#xff1f;下面就为大家详细解读新政策&#xff0c;并提供实用的应对策略。 最引人注目的变化当属中职旅游类专业学生的报考政策。以往&#xff0c;中专学历报考…

【物联网】基于LORA组网的远程环境监测系统设计(ThingsCloud云平台版)

演示视频: 基于LORA组网的远程环境监测系统设计(ThingsCloud云平台版) 前言:本设计是基于ThingsCloud云平台版,还有另外一个版本是基于机智云平台版本,两个设计只是云平台和手机APP的区别,其他功能都一样。如下链接: 【物联网】基于LORA组网的远程环境监测系统设计(机…

SQL 函数进行左边自动补位fnPadLeft和FORMAT

目录 1.问题 2.解决 方式1 方式2 3.结果 1.问题 例如在SQL存储过程中&#xff0c;将1 或10 或 100 长度不足的时候&#xff0c;自动补足长度。 例如 1 → 001 10→ 010 100→100 2.解决 方式1 SELECT FORMAT (1, 000) AS FormattedNum; SELECT FORMAT(12, 000) AS Form…

Nacos简介—2.Nacos的原理简介

大纲 1.Nacos集群模式的数据写入存储与读取问题 2.基于Distro协议在启动后的运行规则 3.基于Distro协议在处理服务实例注册时的写路由 4.由于写路由造成的数据分片以及随机读问题 5.写路由 数据分区 读路由的CP方案分析 6.基于Distro协议的定时同步机制 7.基于Distro协…

中电金信联合阿里云推出智能陪练Agent

在金融业加速数智化转型的今天&#xff0c;提升服务效率与改善用户体验已成为行业升级的核心方向。面对这一趋势&#xff0c;智能体与智能陪练的结合应用&#xff0c;正帮助金融机构突破传统业务模式&#xff0c;开拓更具竞争力的创新机遇。 在近日召开的阿里云AI势能大会期间&…

十分钟恢复服务器攻击——群联AI云防护系统实战

场景描述 服务器遭遇大规模DDoS攻击&#xff0c;导致服务不可用。通过群联AI云防护系统的分布式节点和智能调度功能&#xff0c;快速切换流量至安全节点&#xff0c;清洗恶意流量&#xff0c;10分钟内恢复业务。 技术实现步骤 1. 启用智能调度API触发节点切换 群联系统提供RE…

LLM量化技术全景:GPTQ、QAT、AWQ、GGUF与GGML

01 引言 本文介绍的是在 LLM 讨论中经常听到的各种量化技术。本文的目的是提供一步一步的解释和代码&#xff0c;让大家可以自己使用这些技术来压缩模型。 闲话少说&#xff0c;我们来研究一下吧&#xff01; 02 Quantization 量化是指将高精度数字转换为低精度数字。低精…

IP的基础知识以及相关机制

IP地址 1.IP地址的概念 IP地址是分配给连接到互联网或局域网中的每一个设备的唯一标识符 也就是说IP地址是你设备在网络中的定位~ 2.IP版本~ IP版本分为IPv4和IPv6&#xff0c;目前我们最常用的还是IPv4~~但是IPv4有个缺点就是地址到现在为止&#xff0c;已经接近枯竭~~&…

本地使用Ollama部署DeepSeek

以下是在本地使用Ollama部署DeepSeek的详细教程&#xff0c;涵盖安装、修改安装目录、安装大模型以及删除大模型的操作步骤。 安装Ollama 1. 系统要求 确保你的系统满足以下条件&#xff1a; 操作系统&#xff1a;macOS、Linux或者Windows。足够的磁盘空间和内存。 2. 安装…

开源项目实战学习之YOLO11:ultralytics-cfg-datasets-Objects365、open-images-v7.yaml文件(六)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 medical - pills.yaml 通常用于配置与医学药丸检测任务相关的参数和信息 Objects365.yaml 用于配置与 Objects365 数据集相关信息的文件。Objects365 数据集包含 365 个不同的物体类别…

23种设计模式-行为型模式之策略模式(Java版本)

Java 策略模式&#xff08;Strategy Pattern&#xff09;详解 &#x1f9e0; 什么是策略模式&#xff1f; 策略模式是一种行为型设计模式&#xff0c;它定义了一系列算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可以互相替换。策略模式让算法独立于使用它的客…

使用 AI Agent 改善师生互动的设计文档

使用 AI Agent 改善师生互动的设计文档 一、引言 1.1 研究背景 当前教育领域的师生互动存在诸多挑战&#xff0c;如教师负担过重、学生个体差异大导致难以满足所有人的需求&#xff0c;以及信息传递延迟等问题。引入AI-Agent能够有效缓解这些问题&#xff0c;通过自动化手段协…

2、Ubuntu 环境下安装RabbitMQ

⼀. 安装Erlang RabbitMqRabbitMq需要Erlang语⾔的⽀持&#xff0c;在安装rabbitMq之前需要安装erlang需要Erlang语⾔的⽀持&#xff0c;在安装rabitMq之前需要安装erlang。 安装erlang # 更新软件包 sudo apt-get update # 安装 erlang sudo apt-get install erlang 查看er…

Node.js 操作 ElasticSearch 完整指南:从安装到实战

本文将手把手教你如何搭建 ElasticSearch 环境&#xff0c;并通过 Node.js 实现高效数据检索。包含 10 个可直接复用的代码片段&#xff0c;助你快速掌握搜索、聚合等核心功能&#xff01; 环境搭建篇 1. ElasticSearch 安装要点 下载 es下载连接 下载下来后&#xff0c;进…

硬核科普丨2025年安全、高效网络准入控制系统深度解析

阳途网络准入控制系统&#xff08;Network Access Control&#xff0c;简称NAC&#xff09;是当代网络安全领域的重要工具&#xff0c;有效防止未经授权的访问和数据泄露&#xff0c;保障网络资源的安全性和完整性。本文将深入探讨阳途网络准入控制系统的的重要性和作用。 一、…

搜索二叉树-key的搜索模型

二叉搜索树(Binary Search Tree, BST)是一种重要的数据结构&#xff0c;它有两种基本模型&#xff1a;Key模型和Key/Value模型。 一、Key模型 1.基本概念 Key模型是二叉搜索树中最简单的形式&#xff0c;每个节点只存储一个键值(key)&#xff0c;没有额外的数据值(value)。这…

安卓四大组件之ContentProvider

目录 实现步骤 代码分析 onCreate insert query ContextHolder Cursor 作用与用法 基本步骤&#xff1a; 可能的面试题&#xff1a;为什么使用Cursor&#xff1f; 为什么使用Cursor 使用Cursor的好处 静态内部类实现单例模式 AnndroidManifest.xml配置信息 注释的…

【HTML】【Web开发】滑动条挑战

最近在思考如何开发一些入门级的迷你游戏&#xff0c;于是抽空写了个HTML的滑动条小游戏。 游戏规则如下&#xff1a; 在[0, 100]区间内随机生成一个目标值&#xff0c;显示为&#xff1a;X% 倒计时 3 秒过后&#xff0c;出现 10 秒的挑战倒计时和【停止】按钮 挑战倒计时结…