leetcode-贪心算法
前言
记录leetcode中几道贪心算法的C++解法,以及思路。
一、455-分配饼干
思路:尽量做到小的饼干分给小胃口的孩子,才能做到满足更多孩子的胃口。所以首先需要对两个数组进行排序,然后遍历比较满足孩子胃口的饼干个数。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());//sort算法默认以非递减顺序排序
sort(s.begin(), s.end());
int i = 0, j = 0;//g[i]记录当前孩子的胃口,s[j]记录当前饼干
int ret = 0;//ret用于记录可以最多满足孩子胃口的个数
while (i < g.size() && j < s.size()) {
if (s[j] >= g[i]) {//如果当前饼干的大小满足当前孩子的胃口
ret++;
i++;//换下一个孩子(下一个孩子的胃口大于等于当前孩子的胃口)
j++;//换下一块饼干(下一块饼干一定大于等于当前饼干,这也是需要排序的原因)
}
else {
//如果当前饼干不满足当前孩子的胃口,换下一块饼干
j++;
}
}
return ret;
}
};
int main()
{
vector<int>g = { 3,1 };
vector<int>s = { 1,2,3 };
cout << Solution().findContentChildren(g, s) << endl;
system("pause");
return 0;
}
二、435-不重叠的区间个数
思路:理解队右侧区间进行排序的原因,用下一个区间的左侧区间和当前右侧区间进行比较,如果下一个区间的左侧区间大于等于当前的右侧区间,说明两个区间不重叠。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Solution {
public:
class myCompare {
public:
bool operator()(const vector<int>&l, const vector<int>&r) {
return l[1] < r[1];
}
};
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() == 0)return 0;//如果数组为空,直接返回0
//需要对数组进行排序,排序的规则是,右侧边界由小到大排列(排序时不需要关注左侧边界)
sort(intervals.begin(), intervals.end(), myCompare());
int n = 1;//n表示最大的不重叠区间的个数
int right = intervals[0][1];//记录当前的右侧区间
for (int i = 1; i < intervals.size(); ++i) {
//如果当前区间的左侧区间大于等于当前的右侧区间,说明这两个区间不重叠,n+1
if (intervals[i][0] >= right) {
n++;
right = intervals[i][1];
}
}
return intervals.size() - n;//删除的个数,等于总的区间数减去不重叠区间数
}
};
三、452-用最少量的箭引爆气球
思路:这个题的本质就是求不重叠区间的个数(注意边界条件)
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Solution {
public:
class myCompare {
public:
bool operator()(const vector<int>&l, const vector<int>&r) {
return l[1] < r[1];
}
};
int findMinArrowShots(vector<vector<int>>& points) {
//右侧区间由小到大进行排序
sort(points.begin(), points.end(), myCompare());
int n=1;
int right = points[0][1];
for (int i = 1; i < points.size(); ++i) {
if (points[i][0] > right) {
n++;
right = points[i][1];
}
}
return n;
}
};
int main()
{
vector<vector<int>>points = { {1,2},{1,2} };
cout << Solution().findMinArrowShots(points) << endl;
system("pause");
return 0;
}
四、406-根据身高重建队列
思路:这种涉及两种特征的一般都需要排序,但是要想清楚对身高进行排序的规则以及对序号的排序规则。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Solution {
public:
class myCompare {
public:
bool operator()(const vector<int>&l, const vector<int>&r) {
if (l[0] == r[0])return l[1] < r[1];
return l[0] > r[0];
}
};
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
//身高高的排在前面,身高相同的序号小的排在前面
sort(people.begin(), people.end(), myCompare());
vector<vector<int>>ret;
for (int i = 0; i < people.size(); ++i) {
ret.insert(ret.begin() + people[i][1], people[i]);
}
return ret;
}
};
int main()
{
vector<vector<int>>people = { {7,0},{4,4},{7,1},{5,0},{6,1},{5,2} };
vector<vector<int>>ans = Solution().reconstructQueue(people);
for (auto m : ans) {
cout << "[" << m[0] << "," << m[1] << "]" << endl;
}
system("pause");
return 0;
}
五、121-买卖股票的最佳时机
思路:卖出股票的日期必须在买入股票日期的后面,利用数据在数组的先后顺序模拟日期的先后顺序,记录最低价格和最大利润。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minprice = (int)1e5;//最低价格
int maxprofit = 0; //最大利润
for (auto m:prices) {
//利用原本在数组中的先后顺序,模拟日期的先后顺序
maxprofit = max(maxprofit, m - minprice);
minprice = min(minprice, m);
}
return maxprofit;
}
};
int main()
{
vector<int>prices = { 7,6,5,4 };
cout << Solution().maxProfit(prices) << endl;
system("pause");
return 0;
}
六、122-买卖股票的最佳时机Ⅱ
思路:当前元素大于前一个元素说明对最大利益有增益作用就保留,如果当前元素不大于前一个元素就不叠加。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 1)return 0;
int maxprofit = 0;
for (int i = 1; i < prices.size(); ++i) {
maxprofit += max(0, prices[i] - prices[i-1]);
}
return maxprofit;
}
};
int main()
{
vector<int>prices = { 1,2,3,4,5};
cout << Solution().maxProfit(prices) << endl;
system("pause");
return 0;
}
七、605-种花问题
思路:记连续的0的个数为count,(count-1)/2表示能够再种花的数量。遇到1的时候就将重置count为0。特别地要考虑第一个元素、最后一个元素为0和只有一个元素的情况。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
class Solution {
public:
bool canPlaceFlowers(vector<int>& flowerbed, int n) {
if (flowerbed.size() == 1) {
if (flowerbed[0] == 0 && n <= 1) {
return true;
}
}
int count = 0;
for (int i = 0; i < flowerbed.size(); ++i) {
if (flowerbed[i] == 0) {
//如果是第一个花盆或者最好一个花盆,额外加添加一个0
if (i == 0||i==flowerbed.size()-1) count++;
count++;
}
else {
n -= (count - 1) / 2;
if (n <= 0) {
return true;
}
count = 0;
}
if (i == flowerbed.size() - 1&& flowerbed[i] == 0) {
//上面程序没有考虑最后一个元素为0结束的情况
n -= (count - 1) / 2;
}
}
return n <= 0;
}
};
int main()
{
vector<int>flowerbed = { 1,0,1,0,0 };
cout << Solution().canPlaceFlowers(flowerbed, 1) << endl;
system("pause");
return 0;
}
八、392-判断子序列
思路:双指针遍历
C++代码如下:
#include<iostream>
using namespace std;
#include<string>
class Solution {
public:
bool isSubsequence(string s, string t) {
int i = 0, j = 0;
while (i < s.length() && j < t.length()) {
if (s[i] == t[j]) {
i++;
j++;
}
else {
j++;
}
}
return i == s.length();
}
};
int main()
{
cout << Solution().isSubsequence("abc", "ab") << endl;
system("pause");
return 0;
}
九、665-非递减数列
思路:主要是将情况考虑全面,例如当出现前一个元素大于后一个元素时,应该删除前面一个元素还是后面一个元素。在删除时,要考虑是否被删除元素前面的元素全部都大于后面的元素,以及被删除的元素为第一个元素或者最后一个元素的情况。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
class Solution {
public:
bool subCheck(vector<int>&nums, int k) {
if (k != 0 && k != nums.size() - 1 && nums[k + 1] < nums[k - 1])return false;
for (int i = k+2; i < nums.size(); i++) {
if (nums[i] < nums[i - 1])return false;
}
return true;
}
bool checkPossibility(vector<int>& nums) {
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] < nums[i - 1]) {
if (!subCheck(nums, i) && !subCheck(nums, i - 1)) {
return false;
}
}
}
return true;
}
};
int main()
{
vector<int>nums = { 3,4,2,3 };
cout << Solution().checkPossibility(nums) << endl;
system("pause");
return 0;
}
十、53-最大子数组和
思路:要发现叠加的规律,如果前面的子数组之和为正,就对当前元素有增益作用。但是不能保证当前元素为正,所以需要用一个变量记录最大值。
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = 0;//记录子数组之和
int maxValue=-1e5;//记录最大值
for (int i = 0; i < nums.size(); ++i) {
//前一个子数组对当前元素是否有增益,如果有就保留
ans = max(nums[i], ans + nums[i]);
//虽然前一个子数组对当前元素可能有增益,但是不能保证目前元素为正
maxValue = max(ans, maxValue);
}
return maxValue;
}
};
int main()
{
vector<int>nums = {5, 4, -1, 7,8};
cout << Solution().maxSubArray(nums) << endl;
system("pause");
return 0;
}
十一、763-划分字母区间
思路:主要是通过一个数组记录各个字符的结束地址
C++代码如下:
#include<iostream>
using namespace std;
#include<vector>
#include<string>
#include<algorithm>
class Solution {
public:
vector<int> partitionLabels(string s) {
int last[26];//用last数组记录字符串元素的最后出现的位置
for (int i = 0; i < s.length(); ++i) {
last[s[i] - 'a'] = i;
}
int right = 0;//元素结束地址
int l = 0;//元素起始地址
vector<int>ret;
for (int i = 0; i < s.length(); ++i) {
right = max(right, last[s[i] - 'a']);//更新结束地址
if (i == right) {
ret.push_back(i - l + 1);//将起始到结束的元素记录到数组中
l = i + 1;//更新起始地址
}
}
return ret;
}
};
int main()
{
vector<int>nums = Solution().partitionLabels("asdfsfsabnininibcopopc");
for (auto m : nums)cout << m << " ";
cout << endl;
system("pause");
return 0;
}