day30 452. 用最少数量的箭引爆气球&&406. 根据身高重建队列&&860. 柠檬水找零

    1. 用最少数量的箭引爆气球
      问题描述
      给定一系列气球的水平直径(以区间 [start, end] 表示),要求用最少数量的箭引爆所有气球。每个箭可以水平射中一个区间内的所有气球。
      代码逻辑
      排序:按照气球区间的右端点升序排序。如果右端点相同,则按左端点升序。
      贪心算法:
      初始化箭的数量为1,当前箭的覆盖范围为第一个气球的右端点。
      遍历排序后的气球,如果当前气球的左端点大于当前箭的覆盖范围,则需要再射一箭,并更新当前箭的覆盖范围为该气球的右端点。
      返回结果:最终箭的数量。
      优化思路
      排序优化:直接使用 Arrays.sort(points, (o1, o2) -> o1[1] - o2[1]),避免显式实现 Comparator,代码更简洁。
      逻辑简化:通过排序保证了右端点的顺序,因此无需额外标记数组,直接贪心选择即可。
      时间复杂度
      排序:O(n log n)
      遍历:O(n)
      总复杂度:O(n log n)
      空间复杂度
      排序:O(log n)(取决于排序算法的实现)
      额外空间:O(1)
点击查看代码
//452. 用最少数量的箭引爆气球
    public int findMinArrowShots(int[][] points) {
        //效率超低 335ms
        /*Arrays.sort(points,new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                long num =(long)o1[1] - o2[1];
                if (num<0){
                    return -1;
                }else if (num>0){
                    return 1;
                }else {
                    return 0;
                }
            }
        });
        int count = 0;
        boolean[] visited = new boolean[points.length];
        for (int i = 0; i < points.length; i++) {
            if (visited[i]) continue;
            visited[i] = true;
            count++;
            for (int j = i+1; j < points.length; j++) {
                if (points[j][0]<=points[i][1]) {
                    visited[j]=true;
                }
            }
        }
        return count;*/
        // 按照区间的右端点升序排序
        Arrays.sort(points,new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[1]<o2[1]){
                    return -1;
                }else if (o1[1]>o2[1]){
                    return 1;
                }else {
                    return 0;
                }
            }
        });
        int count = 1; // 箭的数量
        int end = points[0][1]; // 当前箭的覆盖范围(右端点)

        for (int i = 1; i < points.length; i++) {
            if (points[i][0] > end) {//不在范围之内就得再射一箭
                count++;
                end = points[i][1];
            }
        }
        return count;
    }

    1. 根据身高重建队列
      问题描述
      给定一个二维数组 people,其中每个元素是一个长度为2的数组 [h, k],表示身高为 h 的人前面有 k 个人比他高或与他同高。要求重建队列。
      代码逻辑
      排序:按照身高降序排序,若身高相同,则按 k 值升序排序。
      插入:遍历排序后的数组,根据 k 值将每个人插入到结果队列的指定位置。
      返回结果:将结果队列转换为二维数组。
      优化思路
      排序优化:直接使用 Arrays.sort(people, (o1, o2) -> o1[0] != o2[0] ? o2[0] - o1[0] : o1[1] - o2[1]),代码更简洁。
      插入优化:使用 ArrayList 的 add(index, element) 方法,保证插入操作的复杂度为 O(n)。
      时间复杂度
      排序:O(n log n)
      插入:O(n^2)(最坏情况下每次插入都需要移动前面的元素)
      总复杂度:O(n^2)
      空间复杂度
      排序:O(log n)
      额外空间:O(n)(用于存储结果队列)
点击查看代码
//406. 根据身高重建队列
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people,new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o2[0] - o1[0]==0){//h大小一样的情况下,k小的一定要在大的前面。不能在大的处理好的情况下,再往它的前面插入大于或等于它的人(即只能往前面插入矮个子)
                    return o1[1] - o2[1];
                }
                return o2[0] - o1[0];
            }
        });
        List<int[]> ans = new ArrayList<>();
        for (int[] person : people) {//在保证前面的人都比它高或一样的情况下,便可以确定它的插入位置
            ans.add(person[1],person);
        }
        return ans.toArray(new int[ans.size()][]);
    }

    1. 柠檬水找零
      问题描述
      柠檬水每杯售价5美元,顾客可能支付5美元、10美元或20美元。要求在每次交易中都能正确找零,判断是否能完成所有交易。
      代码逻辑
      计数:使用一个数组 nums 分别记录5美元、10美元和20美元的数量。
      遍历账单:
      如果收到5美元,直接增加5美元的数量。
      如果收到10美元,增加10美元的数量,并减少5美元的数量(找零)。
      如果收到20美元,优先使用10美元和5美元找零,如果没有10美元,则使用3张5美元找零。
      检查:如果在任何时刻5美元的数量小于0,则返回 false。
      返回结果:如果所有交易都能完成,返回 true。
      优化思路
      变量命名:使用更具语义的变量名(如 five、ten、twenty),使代码更易读。
      逻辑简化:直接用变量记录数量,避免使用数组,减少空间复杂度。
      时间复杂度
      遍历账单:O(n)
      空间复杂度
      额外空间:O(1)(仅使用了几个变量)
点击查看代码
//860. 柠檬水找零
    public boolean lemonadeChange(int[] bills) {
        //2ms
        int[] nums = new int[3];
        for (int i = 0; i < bills.length; i++) {
            if (bills[i] == 5) {
                nums[0]++;
            } else if (bills[i] == 10) {
                nums[1]++;
                nums[0]--;
            }else {
                nums[2]++;
                if (nums[1]>0){
                    nums[0]--;
                    nums[1]--;
                }else {
                    nums[0]-=3;
                }
            }
            if (nums[0] < 0) return false;
        }
        return true;
        //1ms,直接用变量名记录效率更高点
        /*public boolean lemonadeChange(int[] bills) {
            // 使用更具语义的变量名
            int five = 0, ten = 0, twenty = 0;

            for (int bill : bills) {
                if (bill == 5) {
                    five++;
                } else if (bill == 10) {
                    ten++;
                    five--; // 需要找零 5 元
                    if (five < 0) return false; // 如果没有足够的 5 元,直接返回 false
                } else {
                    twenty++;
                    // 尝试优先使用 10 元 + 5 元找零
                    if (ten > 0) {
                        ten--;
                        five--;
                    } else {
                        five -= 3; // 如果没有 10 元,尝试用 3 张 5 元找零
                    }
                    if (five < 0) return false; // 如果没有足够的 5 元,直接返回 false
                }
            }
            return true;
        }*/
    }
posted @ 2025-02-23 18:38  123木头人-10086  阅读(12)  评论(0)    收藏  举报