JZX轻语:简
LeetCode 2502 - 设计内存分配器
发表于2025年02月25日
不难想,但实现起来还是需要注意很多细节。首先,我们需要一个链表来记录未使用的区块,每次分配内存时,我们需要遍历链表找到一个合适的区块。其次,我们需要一个哈希表来记录已使用的区块,以便于释放内存时能够快速找到对应的区块。最后,我们在释放内存时,将归还的内存作为一个新的区块插入到链表中,然后需要注意合并左右相邻的区块。
struct Node {
Node* next;
Node* prev;
int size;
int pos;
Node(int pos, int size, Node* prev = nullptr, Node* next = nullptr) : pos(pos), size(size), prev(prev), next(next) {}
};
class Allocator {
public:
Allocator(int n) {
head = new Node(0, n);
}
//// Helpers ////
Node* make_dummy_head() {
Node* dummy_head = new Node(-1, -1, nullptr, head);
if (head) head->prev = dummy_head;
return dummy_head;
}
void remove_dummy_head(Node* dummy_head) {
head = dummy_head->next;
if (head) head->prev = nullptr;
delete dummy_head;
}
int allocate(int size, int mID) {
auto* cur = head;
while (cur && cur->size < size) cur = cur->next;
if (!cur) return -1;
// 找到一个空间合适的未使用区块
int target_pos = cur->pos;
allocated[mID].emplace_back(target_pos, size);
Node* dummy_head = make_dummy_head();
// 更新未使用区块信息
if (cur->size == size) { // 区块刚好合适
// 从链表中移除掉
if (cur->prev) cur->prev->next = cur->next;
if (cur->next) cur->next->prev = cur->prev;
} else {
// 剥离掉一部分,更新该区块位置长度
cur->pos += size;
cur->size -= size;
}
remove_dummy_head(dummy_head);
return target_pos;
}
int freeMemory(int mID) {
if (!allocated.count(mID)) return 0;
auto&& blocks = allocated[mID];
// 按pos排序已使用的区块
std::sort(blocks.begin(), blocks.end());
Node* dummy_head = make_dummy_head();
auto* cur = dummy_head;
int free_unit_cnt = 0;
// 逐个归还区块
for (int i = 0; i < blocks.size(); ++i) {
auto [pos, size] = blocks[i];
free_unit_cnt += size;
while (cur->next && cur->next->pos < pos) cur = cur->next;
// 对于归还的内存,新建个区块
auto* new_block = new Node(pos, size, cur, cur->next);
if (new_block->prev) new_block->prev->next = new_block;
if (new_block->next) new_block->next->prev = new_block;
// 合并连续区块
// 看左边
if (new_block->prev && new_block->prev->pos + new_block->prev->size == pos) {
auto* merged = new_block->prev; // 使用左侧区块作为合并结果
merged->size += size;
// 更新前后指向
merged->next = new_block->next;
if (merged->next) merged->next->prev = merged;
// 删除掉刚刚新建的区块,我们直接使用左边的区块就好了
delete new_block;
new_block = merged;
}
// 看右边
if (new_block->next && new_block->pos + new_block->size == new_block->next->pos) {
auto* dropped = new_block->next; // 遗弃掉右边的区块节点,使用新区快作为合并结果
new_block->size += dropped->size;
// 更新前后指向
new_block->next = dropped->next;
if (new_block->next) new_block->next->prev = new_block;
// 删除右边的区块节点
delete dropped;
}
}
remove_dummy_head(dummy_head);
blocks.clear();
return free_unit_cnt;
}
private:
// 使用链表记录未使用的区块
Node* head;
// 使用哈希表记录已使用的区块 mId -> 区块信息向量
unordered_map<int, vector<pair<int, int>>> allocated;
};