前言
大家好,我是jiantaoyab,这篇文章将给大家介绍贪心算法和贪心算法题目的练习和解析,贪心算法的本质就是每一个阶段都是局部最优,从而实现全局最优。加上这篇文章一共有30道贪心题目了,加油!
坏了的计算器
题目分析
所以,只要tar<= sV,无论是奇数还是偶数都加1,当tar>sV时,奇数+1,偶数/2。
代码
class Solution {
public:
int brokenCalc(int startValue, int target) {
int count = 0;
while(target > startValue)
{
if(target % 2 == 0) target /= 2;
else target += 1;
count++;
}
//剩下target <= startValue 都是+1的
//所以startValue - target 统计出有多少个1
return count + startValue - target;
}
};
合并区间
题目分析
这种问题叫区间问题,一般先排序。这里用左端点排序。
证明:能够合并的区间都是连续的
代码
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> ret;
//排序
sort(intervals.begin(), intervals.end());
int left = intervals[0][0], right = intervals[0][1];
for(int i = 0; i < intervals.size(); i++)
{
int l = intervals[i][0], r = intervals[i][1];
if(l <= right)
{
//有重叠部分
right = max(right, r);
}
else
{
//没有重叠部分
ret.push_back({left, right});
left = l;
right = r;
}
}
ret.push_back({left, right});
return ret;
}
};
无重叠区间
贪心的策略如果有重叠区间就移除右断点大的区间,如果没重叠区间的话说明这个区间不会和后面的区间重叠就保留下来,接着用下一个区间去和后面的区间判断。
代码
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
int count = 0;
sort(intervals.begin(), intervals.end());
int left = intervals[0][0], right = intervals[0][1];
for(int i = 1; i < intervals.size(); i++)
{
int l = intervals[i][0], r = intervals[i][1];
if(l < right)
{
//有重叠部分
count++;
right = min(right, r);
}
else
{
//没有重叠部分
right = r;
}
}
return count;
}
};
用最少数量的箭引爆气球
题目分析
这道题目和合并区间求的不一样,我们这里求的是交集。
代码
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
sort(points.begin(), points.end());
int n = points.size();
int ret = 1; //默认是有一个区间去和下一个区间进行比较
int right = points[0][1];
for(int i = 0; i < n; i++)
{
int l = points[i][0], r = points[i][1];
if(l <= right)
{
//有交集
right = min(r, right);
}
else
{
//没有交集
ret++;
right = r;
}
}
return ret;
}
};
整数替换
题目分析
代码
贪心思想来做
class Solution {
public:
int integerReplacement(int n) {
int count = 0;
while(n != 1)
{
if(n % 2 == 0)
{
n /= 2;
count++;
}
else
{
if(n == 3)
{
n = 1;
count += 2;
}
else if(n % 4 == 3) //说明是...11
{
n = n / 2 + 1;
count += 2;
}
else //....01
{
count += 2;
n /= 2;
}
}
}
return count;
}
};
用记忆化搜索做
class Solution {
unordered_map<int, int> hash;
public:
int dfs(long long n)
{
if(hash[n]) return hash[n];
if(n == 1)
{
hash[1] = 0;
return 0;
}
if(n % 2 == 0)
{
hash[n] = 1 + dfs(n / 2);
return hash[n];
}
else
{
hash[n] = 1 + min(dfs(n + 1), dfs(n - 1));
return hash[n];
}
}
int integerReplacement(int n) {
return dfs(n);
}
};
俄罗斯套娃信封问题
题目分析
代码
不是很理解的可以看看第一篇贪心算法
class Solution {
public:
int maxEnvelopes(vector<vector<int>>& envelopes) {
sort(envelopes.begin(), envelopes.end(), [&](const vector<int>&v1, const vector<int>&v2)
{
return v1[0] != v2[0] ? v1[0] < v2[0] : v1[1] > v2[1];
});
vector<int> ret;
ret.push_back(envelopes[0][1]);
for(int i = 1; i < envelopes.size(); i++)
{
int r = envelopes[i][1];
if(r > ret.back())
{
ret.push_back(r);
}
else
{
int left = 0, right = ret.size() - 1;
while(left < right)
{
int mid = (left + right) / 2;
if(ret[mid] >= r) right = mid;
else left = mid + 1;
}
ret[left] = r;
}
}
return ret.size();
}
};
可被三整除的最大和
题目分析
引入一个新问题,如何求出最小值和次小值
代码
class Solution {
public:
int maxSumDivThree(vector<int>& nums) {
const int INF = 0x3f3f3f3f;
int x1 = INF, x2 = INF, y1 = INF, y2 = INF;
int sum = 0;
for(auto x : nums)
{
sum += x;
if(x % 3 == 1)
{
if(x < x1)
{
x2 = x1;
x1 = x;
}
else if(x < x2) x2 = x;
}
else if(x % 3 == 2)
{
if(x < y1)
{
y2 = y1;
y1 = x;
}
else if(x < y2) y2 = x;
}
}
if(sum % 3 == 0) return sum;
else if(sum % 3 == 1) return max(sum - x1, sum - y1 - y2);
else return max(sum - y1, sum - x1 - x2);
}
};
距离相等的条形码
题目分析
把数字间隔1个数字放,先放次数最多的,放下的数字的顺序怎么放都可以。
证明
题目一定是有解的,那么出现最多的那个数字不能超过 n + 1 / 2次,因为如果有n个抽屉,放n + 1 个东西,必然有一个抽屉放2个,分情况讨论。
如果最多出现的数字次数刚好是 n + 1 / 2,那么剩下的随便放,都不相邻
如果出现的数字次数小于 n + 1 / 2,那更不可能相邻了,如果有相邻的说明这个数字出现的次数一定比 这个数字出现的次数多,那它就是最多出现次数的数字了,所以先放次数最多的,放下的数字的顺序怎么放都可以。
代码
class Solution {
public:
vector<int> rearrangeBarcodes(vector<int>& barcodes) {
unordered_map<int, int> hash;
int n = barcodes.size();
int max_val = 0, max_count = 0;
for(auto x : barcodes)
{
if(max_count < ++hash[x])
{
max_val = x;
max_count = hash[x];
}
}
vector<int> ret(n);
int index = 0;
//先放出现次数最多的数字
for(int i = 0; i < max_count; i++)
{
ret[index] = max_val;
index += 2;
}
//处理剩下的数字
hash.erase(max_val);
for(auto& [x, y] : hash)
{
for(int i = 0; i < y; i++)
{
if(index >= n)index = 1;
ret[index] = x;
index += 2;
}
}
return ret;
}
};
重构字符串
代码
这道题目和上一题的区别就是这道题目可能是没有解的。
class Solution {
public:
string reorganizeString(string s) {
int hash[26] = {0};
int n = s.size();
char max_ch = ' ';
int max_count = 0;
for(const auto ch : s)
{
if(max_count < ++hash[ch - 'a'])
{
max_count = hash[ch - 'a'];
max_ch = ch;
}
}
if(max_count > (n + 1) / 2 ) return "";
string ret(n, ' ');
int index = 0;
//先放最多的英文
for(int i = 0; i < max_count; i++)
{
ret[index] = max_ch;
index += 2;
}
hash[max_ch - 'a'] = 0;
//放剩下的英文
for(int i = 0; i < 26; i++)
{
for(int j = 0; j < hash[i]; j++)
{
if(index >= n) index = 1;
ret[index] = 'a' + i;
index += 2;
}
}
return ret;
}
};