【力扣一刷】代码随想录day35(贪心算法part4:860.柠檬水找零、406.根据身高重建队列、452. 用最少数量的箭引爆气球 )

【860.柠檬水找零】简单题(很简单)

思路:

  • 5元的直接收,不需要找钱
  • 10元的只需要找5元
  • 20元的优先找10元,没有10元再全部找5元
class Solution {
    public boolean lemonadeChange(int[] bills) {
        int five = 0;
        int ten = 0;
        for (int i = 0; i < bills.length; i++){
            if (bills[i] == 5){
                five++;
            }
            else if (bills[i] == 10){
                if (five >= 1){
                    five--;
                    ten++;
                }
                else return false;
            }
            else{
                // 如果是付了20,则需要找15元,优先找10元
                if (ten >= 1 && five >= 1){
                    ten--; // 10元1张
                    five--; // 5元1张
                }
                else if (five >= 3){
                    five -= 3; // 5元3张
                }
                else return false;
            }
        }
        return true;
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)


【406.根据身高重建队列】中等题(掌握思路后简单)

思路:先按身高 h 从大到小排序,再依照 k 调整顺序

关键:按照身高降序排序后,即使将后面的数组调整到前面,也不会影响前面的 k ,因为前面的肯定比后面的高。

示例:

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        // 先按身高h降序排序,身高相同的情况下,按k升序排序
        Arrays.sort(people, (o1, o2) -> {
            int res = Integer.compare(o2[0], o1[0]); // h降序
            if (res != 0) return res;
            else return Integer.compare(o1[1], o2[1]); // k升序
        });

        // 频繁按索引插入,LinkedList(基于链表)的效率高于ArrayList(基于数组)
        List<int[]> list = new LinkedList<>(); 

        for (int i = 0; i < people.length; i++){
            list.add(people[i][1], people[i]);  // 按照k调整,相当于从左到右按k为索引在链表中插入对应数组
        }
        return list.toArray(new int[people.length][]); // 链表 -> 数组,返回数组,注意要指定数组的类型和长度
    }
}
  • 时间复杂度: O(nlogn),快速排序
  • 空间复杂度: O(n),额外的链表空间消耗

总结:

1、排序需要重写compare方法,先按身高h降序排序,身高相同的情况下,再按k升序排序

2、调整顺序如果用ArrayList实现,时间复杂度会偏高,最好用LinkedList,而且要想到按k调整顺序相当于按k为索引,依次将people排序后的各个数组插入链表。

3、链表转换为数组的时候,注意toArray()方法默认返回的是Object[]型数组,需要在参数列表中用构造器指定数组的类型和长度。


【452. 用最少数量的箭引爆气球】中等题(偏简单)

思路:

1、按气球的起始位置(左边界)排序

2、使用变量shoot记录上一支箭的范围,判断当前遍历的气球范围是否与上一支箭的范围存在重叠区域:

  • 不存在重叠区域:需要增加一支箭,shoot更新为当前遍历的气球范围。
  • 存在重叠区域:不需要增加箭,只更新shoot的范围,起始位置不变,结束位置取上一个shoot的结束位置和当前遍历气球的结束位置的最小值。
class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points, (o1, o2) -> Integer.compare(o1[0], o2[0]));
        int[] shoot = points[0]; // 用于记录上一次射箭的范围
        int res = 1; // 至少有一个气球,至少射1支箭,初始化为1

        for (int i = 1; i < points.length; i++){
            // 必然成立shoot[0] < point[0]
            int[] point = points[i];
            // 当前遍历的气球不在上一支箭的范围内,需要多加一支箭,并更新射箭的范围
            if (shoot[1] < point[0]){
                res++;
                shoot = point;
            }
            // 当前遍历的气球在上一支箭的范围内,需继续用上一支箭即可,缩窄上一支箭的范围
            else {
                shoot[0] = point[0];
                shoot[1] = Math.min(shoot[1], point[1]);
            }
        }
        return res;
    }
}

  • 时间复杂度: O(nlogn),快速排序
  • 空间复杂度: O(1)
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值