【Py/Java/C++三种语言OD2023C卷真题】20天拿下华为OD笔试之【贪心】2023C-堆内存申请【欧弟算法】全网注释最详细分类最全的华为OD真题题解

73 篇文章 4 订阅
51 篇文章 2 订阅

有LeetCode算法/华为OD考试扣扣交流群可加 948025485
可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳 od1336了解算法冲刺训练

题目描述与示例

题目描述

有一个总空间为100字节的堆,现要从中新申请一块内存,内存分配原则为优先紧接着前一块已使用内存分配空间且最接近申请大小的空闲内存

输入描述

1行是1个整数,表示期望申请的内存字节

2N行是用空格分割的两个整数,表示当前已分配的内存的情况,每一行表示一块已分配的连续内存空间,每行的第1和第2个整数分别表示偏移地址和内存块大小,如:0 1 3 2分别表示0偏移地址开始的1个字节和3偏移地址开始的2个字节已被分配,其余内存空闲。

输出描述

若申请成功,输出申请到内存的偏移;若申请失败,输出-1

补充说明

  1. 若输入信息不合法或无效,则申请失败。
  2. 若没有足够的空间供分配,则申请失败。
  3. 堆内存信息有区域重叠或有非法值等都是无效输入。

示例一

输入

1
0 1
3 2

输出

1

说明

堆中已使用的两块内存是偏移从0开始1字节和偏移从3开始的2字节,空闲的两块内存是偏移从1开始2个字节和偏移从5开始的95个字节,根据分配原则,新申请的内存应从1开始分配1个字节,所以输出移为1

示例二

输入

1
0 1
3 2
6 1

输出

5

解题思路

做出示例二对应的示意图,为

暂时无法在飞书文档外展示此内容

若我们想要申请一块长度为1的内存,申请一块空闲大小足够且尽量靠近1的内存,从图上很容易看出应该选择偏移地址为5。即

暂时无法在飞书文档外展示此内容

若我们想要申请一块长度为2的内存,申请一块空闲大小足够且尽量靠近2的内存,从图上很容易看出应该选择偏移地址为1。即

暂时无法在飞书文档外展示此内容

若我们想要申请一块长度为3的内存,申请一块空闲大小足够且尽量靠近3的内存,从图上很容易看出应该选择偏移地址为7。即

暂时无法在飞书文档外展示此内容

所以在理解了题意之后,题目实际上是非常简单的。

首先我们需要判断所有的区间是否有重叠,如果出现重叠则需要输出-1

intervals.sort(key = lambda x: x[0])
isOverlap = False

for i in range(1, len(intervals)):
    start = intervals[i][0]
    pre_end = intervals[i-1][1]
    if start < pre_end:
        isOverlap = True
        break

在确保没有区间存在重叠之后,则需要查看所有空闲内存,即上述图中的蓝色方块部分。找到所有蓝色方块中最接近待申请内存大小size的那个位置。

显然我们可以通过两个相邻间隔中,前一个间隔的end,和后一个间隔的start,来得到空闲内存。

暂时无法在飞书文档外展示此内容

我们只需要遍历所有相邻间隔,考虑空闲内存的大小,找到大于等于且最接近size的那块空闲内存即为答案。

注意,由于空闲内存有可能出现在最开头或末尾,为了维护算法的一致性,我们可以在intervals数组的最开头和最末尾分别填充哨兵间隔[-1, 0][100, 101],注意填充的数组只有0100是要被使用到的。

整体代码如下

ans = -1
free_size = 100
intervals = [[-1, 0]] + intervals + [[100, 101]]
for i in range(0, len(intervals)-1):
    end = intervals[i][1]
    nxt_start = intervals[i+1][0]
    if free_size > nxt_start - end >= size:
        ans = end
        free_size = nxt_start - end 
print(ans)

代码

Python

# # 题目:【贪心】2023C-堆内存申请
# # 分值:100
# # 作者:闭着眼睛学数理化
# # 算法:区间类贪心
# # 代码看不懂的地方,请直接在群上提问


# 输入将分配的内存的大小
size = int(input())
# 储存所有间隔
intervals = list()

# 行数未知,使用try-except语句进行输入
while True:
    try:
        # 输入某块内存的起始位置、长度
        start, memory_size = map(int, input().split())
        # 记录该间隔
        intervals.append([start, start + memory_size])
    except:
        break

# 检查所输入的间隔是否存在重叠,若存在,则判定为无效申请,直接输出-1
# 将所有间隔按照起始位置start排序
intervals.sort(key = lambda x: x[0])
# 设置一个标志
isOverlap = False
# 遍历所有相邻间隔,如果出现第i个间隔的start位于第i-1个间隔的end之前
# 即出现了重叠,将isOverlap标记为True

for i in range(1, len(intervals)):
    start = intervals[i][0]
    pre_end = intervals[i-1][1]
    if start < pre_end:
        isOverlap = True
        break

# 如果出现重叠,说明是无效输入,直接输出-1
if isOverlap:
    print(-1)
else:
    ans = -1
    # 初始化最接近size的空闲内存的大小为一个较大数
    # 用来维护遍历过程中,找到所有空闲内存中最接近size的那个
    free_size = 100
    # 往intervals的前面和后面分别添加区间[-1, 0]和[100, 101]
    # 这个技巧能够维护最开始的空闲内存和最后的那段空闲内存,都能保持一致性
    # 此时某个间隔的end和下一个间隔的start,会构成一个空闲内存
    intervals = [[-1, 0]] + intervals + [[100, 101]]
    # 考虑所有间隔和其下一个间隔(所有相邻间隔)
    for i in range(0, len(intervals)-1):
        end = intervals[i][1]
        nxt_start = intervals[i+1][0]
        # 当当前空闲内存满足以下两个条件:
        # 1. 大于等于待分配的内存size
        # 2. 小于之前记录过的最大空闲内存大小(更接近size)
        # 则更新答案为end,同时更新free_size
        if free_size > nxt_start - end >= size:
            ans = end
            free_size = nxt_start - end
    print(ans)

Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int size = scanner.nextInt();
        List<int[]> intervals = new ArrayList<>();

        while (true) {
            try {
                int start = scanner.nextInt();
                int memorySize = scanner.nextInt();
                intervals.add(new int[]{start, start + memorySize});
            } catch (Exception e) {
                break;
            }
        }

        intervals.sort(Comparator.comparingInt(a -> a[0]));
        boolean isOverlap = false;

        for (int i = 1; i < intervals.size(); i++) {
            int start = intervals.get(i)[0];
            int preEnd = intervals.get(i - 1)[1];
            if (start < preEnd) {
                isOverlap = true;
                break;
            }
        }

        if (isOverlap) {
            System.out.println(-1);
        } else {
            int ans = -1;
            int freeSize = 100;
            intervals.add(0, new int[]{-1, 0});
            intervals.add(new int[]{100, 101});

            for (int i = 0; i < intervals.size() - 1; i++) {
                int end = intervals.get(i)[1];
                int nextStart = intervals.get(i + 1)[0];
                if (freeSize > nextStart - end && nextStart - end >= size) {
                    ans = end;
                    freeSize = nextStart - end;
                }
            }
            System.out.println(ans);
        }
    }
}

C++

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int size;
    cin >> size;
    vector<vector<int>> intervals;
    
    while (true) {
        int start, memorySize;
        cin >> start >> memorySize;
        if (cin.fail()) break;
        intervals.push_back({start, start + memorySize});
    }

    sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) {
        return a[0] < b[0];
    });

    bool isOverlap = false;

    for (int i = 1; i < intervals.size(); i++) {
        int start = intervals[i][0];
        int preEnd = intervals[i - 1][1];
        if (start < preEnd) {
            isOverlap = true;
            break;
        }
    }

    if (isOverlap) {
        cout << -1 << endl;
    } else {
        int ans = -1;
        int freeSize = 100;
        intervals.insert(intervals.begin(), {-1, 0});
        intervals.push_back({100, 101});

        for (int i = 0; i < intervals.size() - 1; i++) {
            int end = intervals[i][1];
            int nextStart = intervals[i + 1][0];
            if (freeSize > nextStart - end && nextStart - end >= size) {
                ans = end;
                freeSize = nextStart - end;
            }
        }
        cout << ans << endl;
    }

    return 0;
}

时空复杂度

时间复杂度:O(NlogN)。排序所需的时间复杂度。

空间复杂度:O(N)intervals所占空间。


华为OD算法/大厂面试高频题算法练习冲刺训练

  • 华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务300+同学成功上岸!

  • 课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

  • 每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!

  • 60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁

  • 可上全网独家的欧弟OJ系统练习华子OD、大厂真题

  • 可查看链接 大厂真题汇总 & OD真题汇总(持续更新)

  • 绿色聊天软件戳 od1336了解更多

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值