-
- 用最少数量的箭引爆气球
问题描述
给定一系列气球的水平直径(以区间 [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[]>() {@Overridepublic 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[]>() {@Overridepublic 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;}
-
- 根据身高重建队列
问题描述
给定一个二维数组 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[]>() {@Overridepublic 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()][]);}
-
- 柠檬水找零
问题描述
柠檬水每杯售价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) {//2msint[] 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;}*/}