455.分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
示例 2:
输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
分析:
确定基本策略:大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子,那么就应该优先满足胃口大的。
「这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩」
- 先将饼干和小孩胃口从大到小进行排序
- 然后在将胃口和饼干进行遍历,并看饼干是否满足小孩的胃口
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end(), [](const int& a, const int& b){
return a>b;
});
sort(s.begin(), s.end(), [](const int& a, const int& b){
return a>b;
});
int count = 0;
int kek = 0;
int kid = 0;
while(kek < s.size() && kid < g.size()){
if(s[kek] >= g[kid]){
count++;
kek++;
kid++;
}else{
kid++;
}
}
return count;
}
};
时间复杂度分析:
用了快速排序:O(NlogN)
对数组进行了一次遍历O(N)
总体为:O(NlogN)
1005.K 次取反后最大化的数组和
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
以这种方式修改数组后,返回数组可能的最大和。
示例 1:
输入:A = [4,2,3], K = 1
输出:5
解释:选择索引 (1,) ,然后 A 变为 [4,-2,3]。
示例 2:
输入:A = [3,-1,0,2], K = 3
输出:6
解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。
示例 3:
输入:A = [2,-3,-1,5,-4], K = 2
输出:13
解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。
思路:
很自然想到,
局部最优: 将负数变为正数,当前值最大。首先对绝对值大的负数进行操作
全局最优: 整个数组的总和值最大。
从局部可以推到出全局最优,且无反例,可以使用贪心算法。
如果全是正数的情况,又可以看做是另一个贪心情况。
局部全局最优: 将最小值反复正负变换,可以使整体最大。
- 将其按照绝对值从大到小进行排序
- 将负数变为正数
- 将最小值反复正负变换,直至k==0
- 求和
class Solution {
public:
int largestSumAfterKNegations(vector<int>& A, int K) {
sort(A.begin(),A.end(),[](const int& a, const int& b){
return abs(a)>abs(b);
});
for(int i = 0; i < A.size(); i++){
if(K==0){break;}
if(A[i]<0){
K--;
A[i] = -A[i];
}
}
while(K--){A[A.size()-1]*=-1;}
int sum = 0;
for(int e:A){sum+=e;}
return sum;
}
};
376. 摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
示例 1:
输入: [1,7,4,9,2,5]
输出: 6
解释: 整个序列均为摆动序列。]。
示例 2:
输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。
示例 3:
示例 3:
输入: [1,2,3,4,5,6,7,8,9]
输出: 2
思路:
统计连续一上一下的数的个数,即波峰波谷的数量
局部最优: 将数组中连续上升或下降的中间部分忽略掉,只关心波峰和波谷
从局部可以推到出全局最优,且无反例,可以使用贪心算法。
如果全是正数的情况,又可以看做是另一个贪心情况。
- 定义两个int型数,记录前一个差值和后一个差值,以此来判断上升还是下降
- 关键点: 如何将连续递增或减中间部分省略掉
- 可以将单调部分continue,在波峰或波谷出在更新pre
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if(nums.empty()){
return 0;
}
int count = 1;
int pre = 0;
for(int i = 0; i < nums.size()-1; i++){
int cur = nums[i+1] - nums[i];
if(pre <=0 && cur > 0){
count++;
pre = cur;
} else if(pre >=0 && cur < 0){
count++;
pre = cur;
}else{
continue;
}
}
return count;
}
};
406 根据身高重建队列
题目描述
思路:
二维数组,同时对两个维度考量,思路会有点混乱,所以我们现需要确定一个维度。
如果我们确定对维度k进行处理。会发现,这既没有解决身高大小问题,也不能解决前面有多少个人比当前元素高这两个问题
所以选择对维度h进行处理,对身高进行从大到小排序,如果身高相等的情况,则对k进行判断,k从小到大
排序完成后,按维度k,进行插入处理,如果k为0,则插入头部,k为2,则插入引索为2的位置
可以看出,该方法需要进行频繁的插入处理。这时如果还用vector,则时间复杂度会特别大
所以,我们应该使用更适合频繁增删的链表。
List的底层是链表实现的,增删效率高
使用vector进行增删的方法
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(),people.end(),[](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>> res;
for(int i = 0; i < people.size(); i++){
res.insert(res.begin()+people[i][1],people[i]);
}
return res;
}
};
使用链表进行增删的方法:
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(),people.end(),[](const vector<int>& a, const vector<int>& b){
if(a[0]==b[0]){
return a[1]<b[1];
}
return a[0]>b[0];
});
list<vector<int>> res;
for(int i = 0; i < people.size(); i++){
int pos = people[i][1];
auto it = res.begin();
//std::list<vector<int>>::iterator it = res.begin();
while(pos){
it++;
pos--;
}
res.insert(it,people[i]);
}
vector<vector<int>> result(res.begin(),res.end());
return result;
}
};
区间问题
452. 用最少数量的箭引爆气球
思路:
尽可能用一只箭射破更多的气球。这样可以联想到贪心算法。
局部最优:用一支箭尽可能多的射破气球
全局最优:局部最优可以推出全局最优,用最少的箭引爆所有气球。而且没有反例
第一箭必须射破,右边界最小值的气球,所以第一箭为右边界最小值的气球的有边界值,并射破其沿途气球
然后再射还未引爆的右边界最小值的气球的有边界值。射破其沿途气球
重复上述步骤,直至所有气球引爆
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
vector<bool> used(points.size(),false);
sort(points.begin(),points.end(),[](const vector<int>& a, const vector<int>& b){
return a[a.size()-1] < b[b.size()-1];
});
int count = 0;
for(int i = 0; i < points.size(); i++){
if(used[i]==true){
continue;
}
count++;
int shoot = points[i][points[i].size()-1];
int j = i + 1;
while(1){
if(j> points.size()-1){
break;
}
if(shoot >= points[j][0]){
used[j] = true;
}else{
break;
}
j++;
}
}
return count;
}
};