文章目录
- Leetcode 860.柠檬水找零
- Leetcode 406.根据身高重建队列
- Leetcode 452. 用最少数量的箭引爆气球
Leetcode 860.柠檬水找零
题目链接:Leetcode 860.柠檬水找零
题目描述: 在柠檬水摊上,每一杯柠檬水的售价为 5
美元。顾客排队购买你的产品,(按账单 bills
支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5
美元、10
美元或 20
美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5
美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills
,其中 bills[i]
是第 i
位顾客付的账。如果你能给每位顾客正确找零,返回 true
,否则返回 false
。
思路: 本题还是很简单的,我们只需要分情况讨论:
- 对于
5
美元的顾客,直接收下即可 - 对于
10
美元的顾客,我们需要判断5
美元的零钱是否大于等于1张 - 对于
20
美元的顾客,我们先判断能否使用10+5
的组合,再判断5
美元的零钱是否大于等于3张(这里体现了贪心思想)
代码如下:
class Solution {
public:bool lemonadeChange(vector<int>& bills) {int five = 0, ten = 0; //只需记录这两种钞票用于找零for (int bill : bills) {if (bill == 5) {five++;}if (bill == 10) {if (five <= 0)return false;ten++;five--;}if (bill == 20) {if (five > 0 && ten > 0) { //贪心策略:优先使用10来找零ten--;five--;} else if (five >= 3) {five -= 3;} elsereturn false;}}return true;}
};
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
Leetcode 406.根据身高重建队列
题目链接:Leetcode 406.根据身高重建队列
题目描述: 假设有打乱顺序的一群人站成一个队列,数组 people
表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki]
表示第 i
个人的身高为 hi
,前面 正好 有 ki
个身高大于或等于 hi
的人。
请你重新构造并返回输入数组 people
所表示的队列。返回的队列应该格式化为数组 queue
,其中 queue[j] = [hj, kj]
是队列中第 j
个人的属性(queue[0]
是排在队列前面的人)。
思路: 本题和Leetcode135 分发糖果这道题很像,都是需要考虑两个维度,而对于两个维度的贪心题目,我们需要先确定一个维度,再确定另一个维度。 如果按照k
来从小到大排序,排完之后,会发现k
的排列并不符合条件,身高也不符合条件,两个维度哪一个都没确定下来。
那么按照身高h
来排序呢?身高一定是从大到小排(身高相同的话则k
小的站前面),让高个子在前面。此时我们可以确定一个维度了,就是身高,前面的节点一定都比本节点高,再按照k
为下标重新插入队列就可以了。
-
局部最优:优先按身高高的
people
的k
来插入。插入操作过后的people
满足队列属性 -
全局最优:最后都做完插入操作,整个队列满足题目队列属性
代码如下:
class Solution {
public://按照身高从大到小排序,身高相同则k小的在前面static bool cmp(const vector<int>& a, const vector<int>& b) {if (a[0] == b[0]) return a[1] < b[1];return a[0] > b[0];}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort (people.begin(), people.end(), cmp);vector<vector<int>> que;for (int i = 0; i < people.size(); i++) {int position = people[i][1];que.insert(que.begin() + position, people[i]);}return que;}
};
由于动态数组vector
底层是普通数组实现的,我们都知道在数组内插入删除元素效率很低,因此可以考虑用链表list
优化。
代码如下:(利用链表list
提高插入效率)
class Solution {
public://按照身高从大到小排序,身高相同则k小的在前面static bool cmp(const vector<int>& a, const vector<int>& b) {if (a[0] == b[0])return a[1] < b[1];return a[0] > b[0];}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort(people.begin(), people.end(), cmp);list<vector<int>> que; // list底层是链表,插入效率高for (int i = 0; i < people.size(); i++) {int position = people[i][1];auto it = que.begin();while (position--) { //寻找插入位置it++;}que.insert(it, people[i]);}return vector<vector<int>>(que.begin(), que.end());}
};
- 时间复杂度: O ( n l o g n + n 2 ) O(nlog n + n^2) O(nlogn+n2)
- 空间复杂度: O ( n ) O(n) O(n)
Leetcode 452. 用最少数量的箭引爆气球
题目链接: Leetcode 452. 用最少数量的箭引爆气球
题目描述: 在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。
一支弓箭可以沿着 x
轴从不同点完全垂直地射出。在坐标 x
处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart
,xend
, 且满足 xstart ≤ x ≤ xend
,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
给你一个数组 points
,其中 points [i] = [xstart,xend]
,返回引爆所有气球所必须射出的最小弓箭数。
思路: 本题看似是二维空间,实际上只需考虑气球的横坐标范围是否重叠就行了,纵坐标只是方便描述弓箭射出的出题背景。首先对气球左边界排序,然后遍历横坐标判断当前的i
和i-1
是否重叠,如果不重叠,result++
;重叠则更新右边界。 这里有一个关键点就是如何更新右边界?我们需要对points[i]
和points[i-1]
的右边界取最小值,只有这样才方便后续继续判断是否重叠。
代码如下:
class Solution {
public:static bool cmp(const vector<int>& a, const vector<int> b) {if (a[0] == b[0])return a[1] < b[1];return a[0] < b[0];}int findMinArrowShots(vector<vector<int>>& points) {if (points.size() == 0)return 0;int result = 1;sort(points.begin(), points.end(), cmp); //按照左边界大小进行排序for (int i = 1; i < points.size(); i++) {if (points[i][0] > points[i-1][1]) { //如果没有重叠,则弓箭数+1result++;} else { //否则更新右边界points[i][1] = min(points[i - 1][1], points[i][1]);}}return result;}
};
- 时间复杂度: O ( n l o g n ) O(nlog n) O(nlogn)
- 空间复杂度: O ( 1 ) O(1) O(1)
不过快速排序的空间复杂度还体现在递归上了:
最优的情况下空间复杂度为: O ( l o g n ) O(logn) O(logn);每一次都平分数组的情况
最差的情况下空间复杂度为: O ( n ) O( n ) O(n) ;退化为冒泡排序的情况
总结: 很多贪心类题目第一次做几乎不太可能想到,因此需要多做题(拓宽视野)+多复习(巩固)
最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!