当前位置: 首页 > article >正文

每日一题-设计内存分配器;详细分析思路以及多解法

#我回归了家人们,我将持续更新,并保证高质量内容#

题目取自LeetCode每日一题2502,大家可自行练习或者验证自己思路


题目

给你一个整数 n ,表示下标从 0 开始的内存数组的大小。所有内存单元开始都是空闲的。

请你设计一个具备以下功能的内存分配器:

  1. 分配 一块大小为 size 的连续空闲内存单元并赋 id mID 。
  2. 释放 给定 id mID 对应的所有内存单元。

注意:

  • 多个块可以被分配到同一个 mID 。
  • 你必须释放 mID 对应的所有内存单元,即便这些内存单元被分配在不同的块中。

实现 Allocator 类:

  • Allocator(int n) 使用一个大小为 n 的内存数组初始化 Allocator 对象。
  • int allocate(int size, int mID) 找出大小为 size 个连续空闲内存单元且位于  最左侧 的块,分配并赋 id mID 。返回块的第一个下标。如果不存在这样的块,返回 -1 。
  • int freeMemory(int mID) 释放 id mID 对应的所有内存单元。返回释放的内存单元数目。

示例:

输入
["Allocator", "allocate", "allocate", "allocate", "freeMemory", "allocate", "allocate", "allocate", "freeMemory", "allocate", "freeMemory"]
[[10], [1, 1], [1, 2], [1, 3], [2], [3, 4], [1, 1], [1, 1], [1], [10, 2], [7]]
输出
[null, 0, 1, 2, 1, 3, 1, 6, 3, -1, 0]

 代码基础框架

class Allocator {

public:

    Allocator(int n) {

       

    }

   

    int allocate(int size, int mID) {

       

    }

   

    int freeMemory(int mID) {

       

    }

};

/**

 * Your Allocator object will be instantiated and called as such:

 * Allocator* obj = new Allocator(n);

 * int param_1 = obj->allocate(size,mID);

 * int param_2 = obj->freeMemory(mID);

 */


 题目分析

目标:设计一个内存分配器,支持分配和释放操作。分配时需要找到最左侧的连续空闲块,释放时需清除所有指定ID的内存单元。

关键要求

我们需要设计一个内存分配器,支持以下功能:

  1. 分配:找到最左侧的连续 size 个空闲内存单元,分配给 mID,并返回起始下标。

  2. 释放:释放所有 mID 对应的内存单元,返回释放的数量。

解题思路

1. 问题分析
  • 内存表示:用一个数组 arr 表示内存单元,arr[i] 的值表示第 i 个内存单元的状态。

    • arr[i] == 0:空闲。

    • arr[i] > 0:已分配给某个 mID

  • 分配逻辑

    • 需要找到最左侧的连续 size 个空闲单元。

    • 如果找到,将其分配给 mID,并返回起始下标;否则返回 -1

  • 释放逻辑

    • 遍历数组,将所有 mID 对应的单元置为 0,并统计释放的数量。

2. 破题关键
  • 如何找到最左侧的连续空闲块

    • 遍历数组,统计连续空闲单元的数量。

    • 当连续空闲单元数量达到 size 时,记录起始位置并分配。

  • 如何高效释放内存

    • 遍历数组,将所有 mID 对应的单元置为 0,并统计数量。


代码逐行解析

1. 初始化
class Allocator {
    int[] arr;

    public Allocator(int n) {
        arr = new int[n]; // 初始化大小为 n 的内存数组,所有单元初始为 0(空闲)
    }
}
  • 作用:初始化一个大小为 n 的内存数组,所有单元初始为 0,表示空闲。


2. 分配方法 allocate
public int allocate(int size, int mID) {
    int cnt = 0; // 计数器,记录当前连续空闲单元的数量
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] > 0) { // 当前单元已被分配
            cnt = 0; // 重置计数器
            continue;
        }
        cnt++; // 当前单元空闲,增加计数器
        if (cnt == size) { // 找到足够的连续空闲单元
            Arrays.fill(arr, i - size + 1, i + 1, mID); // 分配内存
            return i - size + 1; // 返回起始下标
        }
    }
    return -1; // 没有找到足够的连续空闲单元
}
  • 逻辑

    1. 初始化计数器 cnt,用于记录当前连续空闲单元的数量。

    2. 遍历数组 arr

      • 如果当前单元已被分配(arr[i] > 0),重置计数器 cnt = 0

      • 如果当前单元空闲(arr[i] == 0),增加计数器 cnt++

      • 当 cnt == size 时,表示找到足够的连续空闲单元:

        • 使用 Arrays.fill 将这段内存单元赋值为 mID

        • 返回这段内存块的起始下标 i - size + 1

    3. 如果遍历完数组仍未找到足够的连续空闲单元,返回 -1

  • 示例

    • 假设 arr = [0, 0, 0, 0, 0],调用 allocate(2, 1)

      • 找到最左侧的连续 2 个空闲单元,分配为 [1, 1, 0, 0, 0],返回 0


3. 释放方法 freeMemory
public int freeMemory(int mID) {
    int size = 0; // 计数器,记录释放的内存单元数量
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] == mID) { // 当前单元属于 mID
            arr[i] = 0; // 释放内存单元
            size++; // 增加计数器
        }
    }
    return size; // 返回释放的内存单元数量
}
  • 逻辑

    1. 初始化计数器 size,用于记录释放的内存单元数量。

    2. 遍历数组 arr

      • 如果当前单元属于 mIDarr[i] == mID),将其释放(设为 0),并增加计数器 size++

    3. 返回 size,表示释放的内存单元数量。

  • 示例

    • 假设 arr = [1, 1, 0, 2, 2],调用 freeMemory(1)

      • 释放所有 mID = 1 的单元,变为 [0, 0, 0, 2, 2],返回 2


复杂度分析

  1. 分配方法 allocate

    • 时间复杂度:O(n),最坏情况下需要遍历整个数组。

    • 空间复杂度:O(1),只使用了常数空间。

  2. 释放方法 freeMemory

    • 时间复杂度:O(n),需要遍历整个数组。

    • 空间复杂度:O(1),只使用了常数空间。

  • 核心思想:通过数组模拟内存单元,遍历处理分配和释放。

  • 优点:实现简单,逻辑清晰。

  • 缺点:在大规模数据下,时间复杂度较高。

  • 优化方向:可以使用更高效的数据结构(如链表或线段树)来管理空闲块,减少时间复杂度。


代码

class Allocator {
    int[] arr;

    public Allocator(int n) {
        arr = new int[n];
    }
    
    public int allocate(int size, int mID) {
        int cnt = 0;
        for(int i = 0; i < arr.length; i++){
            if(arr[i] > 0){
                cnt = 0;
                continue;
            }
            cnt++;
            if(cnt == size){
                Arrays.fill(arr, i - size + 1, i + 1, mID);
                return i - size + 1;
            }
        }
        return -1;
    }
    
    public int freeMemory(int mID) {
        int size = 0;
        for(int i = 0; i < arr.length; i++){
            if(arr[i] == mID){
                arr[i] = 0;
                size++;
            }
        }
        return size;
    }
}

/**
 * Your Allocator object will be instantiated and called as such:
 * Allocator obj = new Allocator(n);
 * int param_1 = obj.allocate(size,mID);
 * int param_2 = obj.freeMemory(mID);
 */


其余思路代码分享

一、Runtime:28ms

class Allocator {
    private int n;
    private int[] memory;

    public Allocator(int n) {
        this.n = n;
        this.memory = new int[n];
    }

    public int allocate(int size, int mID) {
        int count = 0;
        for (int i = 0; i < n; ++i) {
            if (memory[i] != 0) {
                count = 0;
            } else {
                ++count;
                if (count == size) {
                    for (int j = i - count + 1; j <= i; ++j) {
                        memory[j] = mID;
                    }
                    return i - count + 1;
                }
            }
        }
        return -1;
    }

    public int freeMemory(int mID) {
        int count = 0;
        for (int i = 0; i < n; ++i) {
            if (memory[i] == mID) {
                ++count;
                memory[i] = 0;
            }
        }
        return count;
    }
}

二、Runtime:8ms

class Allocator {
    int[] arr; // 存放数据, arr[i]到arr[i + len[i] - 1]范围内都是arr[i]数据
    int[] len; // 存放数据长度, len[i]不等于0才有效, 表示arr[i]数据的长度

    public Allocator(int n) {
        this.arr = new int[n]; // 初始化默认都是0
        this.len = new int[n];
        this.len[0] = n; // 初始mem[0]数据的长度就是n
    }
    
    public int allocate(int size, int mID) {
        for (int i = 0; i < arr.length; i += len[i]) {
            if (arr[i] == 0 && len[i] >= size) {
                // 如果当前数据是0且范围足够
                if (i + size < arr.length && arr[i + size] == 0) {
                    // 超过size范围之后还是0,说明当前范围超过size,
                    // 也可以直接用len[i] > size判断,更新0的长度
                    len[i + size] = len[i] - size;
                }
                arr[i] = mID;
                len[i] = size;
                return i;
            }
        }
        return -1;
    }
    
    public int freeMemory(int mID) {
        int res = 0;
        // 先删掉数据
        for (int i = 0; i < arr.length; i += len[i]) {
            if (arr[i] == mID) {
                res += len[i];
                arr[i] = 0;
            }
        }
        // 删掉后为0的范围和其相邻的本来就是0的范围合并
        for (int i = 0; i < arr.length; i += len[i]) {
            if (arr[i] == 0) {
                int j = i + len[i];
                while (j < arr.length && arr[j] == 0) {
                    j += len[j];
                }
                len[i] = j - i;
            }
        }
        return res;
    }
}

/**
 * Your Allocator object will be instantiated and called as such:
 * Allocator obj = new Allocator(n);
 * int param_1 = obj.allocate(size,mID);
 * int param_2 = obj.freeMemory(mID);
 */

制作不易,感谢你的查阅、关注、评论和点赞


http://www.kler.cn/a/561355.html

相关文章:

  • 企业业务安全进阶之路:AI技术与数据分析的应用
  • JavaWeb 学习笔记
  • 个人电脑小参数GPT预训练、SFT、RLHF、蒸馏、CoT、Lora过程实践——MiniMind图文版教程
  • linux--多进程开发基础(3) exec函数族
  • JavaWeb-GenericServlet源码分析(适配器/模板方法)
  • Sui 通过 SCION 推进网络安全与性能
  • Log | Hugo+PaperMod+Github创建自己的博客网站
  • 在ubuntu如何安装samba软件?
  • MongoDB03 - MongoDB索引,事务和安全
  • mac下载MAMP6.8.1
  • 可重入与可重入锁:多线程编程中的安全卫士
  • rust学习笔记6-数组练习704. 二分查找
  • MySQL数据,查询QPS,TPS 数据
  • 二分查找理解
  • xss-lab
  • 计算机视觉算法实战——异常检测(主页有源码)
  • OpenGL 03--顶点着色器、片段着色器、元素缓冲对象
  • 刷题日记5
  • 【TVM教程】为 NVIDIA GPU 自动调度神经网络
  • 【STM32】使用电打火器测试火焰传感器,去掉传感器LED依然亮