理解算法(1): 最大值,最小值,和堆。
最近总想,算法好像没有数学那样直观,例如方程可以解决一大类问题,我们遇到许多数学问题,只要将其转成方程问题,剩下的就是解方程。算法好像不是那么直观,顺着这个思路开始重新看算法问题。今天有一个收获,也可能其他人早就知道。
int max=INT_MIN;
for(size_t i=0;i<v.size();i++){
if(v[i]>max){
max=v[i];
}
}
int min=INT_MAX;
for(size_t i=0;i<v.size();i++){
if(v[i]<min){
min=v[i];
}
}
我今天才意识到,这里的
a)int max=INT_MIN; 和 if(v[i]>max)
b)int min=INT_MAX; 和 if(v[i]<min)
居然,分别就是隐含的堆。其中,
a) int max=INT_MIN;
是元素只有一个的最大堆。
b) int min=INT_MAX;
是元素只有一个的最小堆。
从这个角度来说,很多利用堆来解决的算法,本质上就和上面这两个for循环一样。
例如,求k个最小值:
// 步骤1,等价于 int min = INT_MAX;
MinHeap heap;
for(size_t i=0;i<k;i++){
heap.push(v[i]);
}
// 步骤2,等价于 if(v[i]<min) 然后交换
for(size_t i=k;i<v.size();i++){
if(v[i]<heap.top()){
heap.pop();
heap.push(v[i]);
}
}
例如,求k个最大值
// 步骤1,等价于 int max = INT_MIN;
MaxHeap heap;
for(size_t i=0;i<k;i++){
heap.push(v[i]);
}
// 步骤2,等价于 if(v[i]>max) 然后交换
for(size_t i=k;i<v.size();i++){
if(v[i]>heap.top()){
heap.pop();
heap.push(v[i]);
}
}
从这个角度来说,似乎:求最大值和最小值,就是堆问题。只是堆的元素可能是1个,也可能是k个,进一步堆每个元素可能是一个向量,就像方程和方程组的关系。
下一个例子是,求多个数组里,覆盖范围最小的区间,使得区间包含了每个数组至少一个元素。例如
输入数据:
[ 3, 6, 8, 10, 15 ]
[ 1, 5, 12 ]
[ 4, 8, 15, 16 ]
[ 2, 6 ]
输出:
4–6
这仍然是一个求「最小值」问题,最小值变成了一个最小区间,基本思路还是
- 使用一个最小堆存区间
- 循环往前迭代,比较新区间是否比当前区间更小,如果是就替换。
如果把lists看成矩阵,
- 初始化:首先取每个数组里最小的元素,一共4个数构成了一个数组
- 更新:删除4个元素里最小的数,同时从这个数所在的原list里取下一个数填充
那么,就会得到一种新的组合数构成的数组
[ {3,1,4,2} {3,5,4,2} {3,5,4,6}, ... ]
对于这个数组,用一个for循环就可以遍历出最小区间,这个区间包含每个list至少一个元素:
MinHeap heap;
int high=0;
pair<int,int> min_range={0,INT_MAX};
for(size_t i=0;i<lists.size(); i++){
auto e = {value=lists[i][0], row=i, column=0};
heap.push(e);
high = max(high, e.value);
}
while(true){
auto e = heap.top();
heap.pop();
low = e.value;
if(high-low<min_range.second-min_range.first){
min_range.first = low;
min_range.second = high;
}
if(e.column+1<lists[e.row].size()){
next_value = lists[e.row][e.column+1];
next_row = e.row;
next_column = e.column+1;
heap.push({value:next_value, row: next_row, column: next_column});
}else{
break;
}
}
从这个角度来说,如何看待「数组」,就是需要重新审视的,正确的角度看“数组”,就可以获得更简洁的算法。
参考资料:
[1] 大部分时候不必自己写个堆数据结构,直接用c++的 make_heap 即可:
en.cppreference.com/w/cpp/algorithm/make_heap
[2] 这里有更多的堆数据结构本身的介绍的习题:
www.geeksforgeeks.org/heap-data-structure/
[3] 至于堆内部是怎么维护数据结构的,倒是其次,可以看这几个链接,随机从网上搜的:
[3.1] opencoursehub.cs.sfu.ca/jackt/grav-cms/cmpt225-1/notes/files/unit16_heap.pdf
[3.2] courses.engr.illinois.edu/cs225/fa2023/assets/assignments/labs/heaps/cs225sp22-lab_heaps-handout-solution.pdf
[3.3] web.stanford.edu/class/archive/cs/cs161/cs161.1168/lecture4.pdf
[3.4] staff.ustc.edu.cn/~csli/graduate/algorithms/book6/chap07.htm
--end--
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2020-10-20 现代编程语言:Python(蛇形遍历一颗树)