LeetCode高频题:Android系统中WakeLock防止手机进入睡眠模式,统计出每个应用对WakeLock的不同贡献值
提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
题目
一个大佬发我的题目,我帮忙看了看
一、审题
示例:
你们有没有觉得案例2有问题啊?
1245–1250,长度不是6啊
是5啊
对吧???????
二、解题
这题关键在哪里?
在俩交叉段中,有交集的你得平均,没有交集的你得算上
比如上面的案例2
1236–1238属于两个应用公有的段
这个只能算平均
3/2=1
没有交集的2个2直接加上
拢共5
但是1245–1250,就是6,没有交集,不平均
故
你最重要的就是搞清楚哪些段有交集?
比如对于当前1234–1240段cur,谁影响了我?
【影响我是啥意思?就是有的段,它的段尾,竟然>=cur的段首】
就是影响我cur
就上面1236–1238段,断尾1238>=cur的段首1234
那它一定影响cur
这种情况
我可以把交集段段首start【就是2段的段首最大值】和断尾end找出来【就是2段的段尾最小值】
没有交集的段首a就是就是2段的段首最小值
没有交集的段尾b就是就是2段的段尾最大值
这样的话,贡献值,就是
start-a+b-end+(end-start)/2
前面减的是非交集的前面段
中间减的是非交集的后面段
最后减的是交集的平均段
懂?
具体代码怎么模拟这个过程呢???
(1)先将所有的时间段变为Node,放入有序表,有序表按照start排序
(2)然后一鼓作气,把能相互影响的那几段,放入小根堆heap
(3)一旦有一段压根不想交的进来的话,先不要进堆,结算heap里面的结果,放入ans
然后回到(2)继续循环就行
最后ans就出来了
手撕代码如下:
import java.util.*;
//看两端交叉,用堆模拟多少人公用
public static class Solution{
//应用占用时间段
public static class Node{
public int start;
public int end;
public Node(int s, int e){
start = s;
end = e;
}
}
//排序比较器,根据start排序
public static class cptr implements Comparator<Node>{
@Override
public int compare(Node o1, Node o2){
return o1.start - o2.start;
}
}
//然后开始模拟业务
public List<Integer> getWakeLockContrib(List<List<Integer>> arr){
TreeSet<Node> set = new TreeSet<>(new cptr());//有序表
for(List<Integer> x:arr){
for (int i = 0; i < x.size(); i+=2) {
set.add(new Node(x.get(i), x.get(i + 1)));//分别加入set
}
}
//然后小根堆模拟交集最大的
PriorityQueue<Node> heap = new PriorityQueue<>(new cptr());//小根堆
List<Integer> ans = new ArrayList<>();//结果
while (!set.isEmpty()) {
while (heap.isEmpty() ||
(!set.isEmpty() && set.first().start <= heap.peek().end)){
heap.add(set.pollFirst());//有交集的进来
}
//不影响影响我cur的,就要结算heap里面的
int start = Integer.MIN_VALUE;
int end = Integer.MAX_VALUE;
int k = 0;
int a = Integer.MAX_VALUE;
int b = Integer.MIN_VALUE;//这个记录非交叉的部分
while (!heap.isEmpty()){
k++;//几个共享
Node node = heap.poll();
start = Math.max(start, node.start);
end = Math.min(end, node.end);//取交集
a = Math.min(a, node.start);//取非交集
b = Math.max(b, node.end);
}
if (start != Integer.MIN_VALUE && end != Integer.MAX_VALUE){
ans.add(b - end + start - a + (end - start) / k);//加入结果
}
}
//最后一定会返回结果
return ans;
}
}
然后测试:
案例1:
public static void main(String[] args) {
List<List<Integer>> arr = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
tmp.add(1234);
tmp.add(1235);
arr.add(tmp);
tmp = new ArrayList<>();
tmp.add(1236);
tmp.add(1238);
arr.add(tmp);
Solution solution = new Solution();
List<Integer> ans = solution.getWakeLockContrib(arr);
for(Integer x:ans) System.out.println(x +" ");
}
结果:
案例2:
public static void main(String[] args) {
List<List<Integer>> arr = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
tmp.add(1234);
tmp.add(1240);
arr.add(tmp);
tmp = new ArrayList<>();
tmp.add(1236);
tmp.add(1238);
tmp.add(1245);
tmp.add(1250);
arr.add(tmp);
Solution solution = new Solution();
List<Integer> ans = solution.getWakeLockContrib(arr);
for(Integer x:ans) System.out.println(x +" ");
}
结果:
非常稳
笔试过程中,可能:会出bug,想想逻辑,调试一下就知道自己错哪里了,慢慢就调试出来了
总结
提示:重要经验:
1)贪心的题目,一般就是排序+堆,典型的这种操作,大厂经常考,据说这个是OPPO23届秋招的题目
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。