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;
}

参考

leetcode题解
leetcode官网

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值