有LeetCode算法/华为OD考试扣扣交流群可加 948025485
可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳od1336
了解算法冲刺训练
题目描述与示例
题目描述
有一个总空间为100
字节的堆,现要从中新申请一块内存,内存分配原则为优先紧接着前一块已使用内存分配空间且最接近申请大小的空闲内存。
输入描述
第1
行是1
个整数,表示期望申请的内存字节
第2
到N
行是用空格分割的两个整数,表示当前已分配的内存的情况,每一行表示一块已分配的连续内存空间,每行的第1
和第2
个整数分别表示偏移地址和内存块大小,如:0 1 3 2
分别表示0
偏移地址开始的1
个字节和3
偏移地址开始的2
个字节已被分配,其余内存空闲。
输出描述
若申请成功,输出申请到内存的偏移;若申请失败,输出-1
补充说明
- 若输入信息不合法或无效,则申请失败。
- 若没有足够的空间供分配,则申请失败。
- 堆内存信息有区域重叠或有非法值等都是无效输入。
示例一
输入
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]
,注意填充的数组只有0
和100
是要被使用到的。
整体代码如下
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
了解更多