一个人的朝圣 — LeetCode打卡第35天
知识总结
贪心算法, 用局部最优推出全局最优
Leetcode 860. 柠檬水找零
题目说明
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
代码说明
记录五元和十元的张数即可.
class Solution {
public boolean lemonadeChange(int[] bills) {
int[] freq = new int[2];
for(int bill : bills){
if(bill == 5){
freq[0]++;
}
else if(bill == 10){
if(freq[0] < 0) return false;
freq[0]--;
freq[1]++;
}else{
if(freq[1] >= 1 && freq[0] >=1){
freq[0]--;
freq[1]--;
}else if(freq[0] >= 3){
freq[0] -= 3;
}else{
return false;
}
}
}
return true;
}
}
Leetcode 452. 用最少数量的箭引爆气球
题目说明
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
代码说明
方法1 :
这道题和之前的合并区间有点像, 可以用类似的方法, 但是这次新加入的区间不是并集, 而是交集. 如果没有相交的部分, 那么新增添一个区间进入list. 学习排序的写法, 直接写减法会存在Interger 最大值的越界问题, 所有重写compare方法.
class Solution {
public int findMinArrowShots(int[][] points) {
if(points.length == 1){
return 1;
}
// Arrays.sort(points,
// (a1, a2) -> a1[0] - a2[0]
// );
// 按照左边界的大小排序
Arrays.sort(points, new Comparator<int[]>(){
public int compare(int[] point1, int[] point2){
if(point1[0]> point2[0]){
return 1;
}else if (point1[0] < point2[0]){
return -1;
}else{
return 0;
}
}
});
LinkedList<int[]> list = new LinkedList<>();
int left =0, right = 0;
list.add(points[0]);
for(int i = 1; i < points.length; i++){
left = points[i][0];
right = points[i][1];
if(left<= list.getLast()[1]){
list.getLast()[0] = Math.max(list.getLast()[0], left);
list.getLast()[1] = Math.min(list.getLast()[1], right);
}else{
list.add(points[i]);
}
}
return list.size();
}
}
方法2;
不同于上面, 我们按照有边界的大小升序排序. 那么为了尽可能多的射中更多的气球, 我们会射的位置为第一个的最右边, 然后如果一个新区间的左边界大于这个pos, 说明原来的箭射不中该区间, 需要重新射出新的箭.
class Solution {
public int findMinArrowShots(int[][] points) {
if(points.length == 1){
return 1;
}
// Arrays.sort(points,
// (a1, a2) -> a1[1] - a2[1]
// );
Arrays.sort(points, new Comparator<int[]>(){
public int compare(int[] point1, int[] point2){
if(point1[1]> point2[1]){
return 1;
}else if (point1[1] < point2[1]){
return -1;
}else{
return 0;
}
}
});
int pos = points[0][1];
int ans = 1;
for(int[] bollon : points){
if(bollon[0] > pos){
pos = bollon[1];
ans++;
}
}
return ans;
}
}
Leetcode 406. 根据身高重建队列
题目说明
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
代码说明
先花时间把题目读懂, 排序后应该的[5, 0] 表示, 这个人身高为5, 前面身高>=5的人有0 个
解题还是挺难的, 然后许多Java代码的技巧和语法糖.
- 先按身高再按k值排序
Arrays.sort(people,
(a, b) -> {
if(a[0] == b[0]) return a[1] - b[1]; // 身高相同, k值升序排列
return b[0] - a[0]; // 否则优先按身高降序排列
}
);
- 指定位置插入元素.
LinkedList.add(index, value)
- list 转为数组, 记得写array的大小
queue.toArray(new int[people.length][]
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people,
(a, b) -> {
if(a[0] == b[0]) return a[1] - b[1]; // 身高相同, k值升序排列
return b[0] - a[0]; // 否则优先按身高降序排列
}
);
LinkedList<int[]> queue = new LinkedList<>();
for(int[] p : people){
queue.add(p[1], p);
}
return queue.toArray(new int[people.length][]);
}
}