贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法。贪心算法通常用来解决最优化问题,其核心思想是通过局部最优解逐步推导出全局最优解。
在贪心算法中,我们并不总是考虑到未来可能发生的情况,而是只关注当前的最优选择。这种贪心选择性质使得贪心算法特别适合解决那些具有最优子结构性质的问题,即局部最优解能够推导出全局最优解的问题。
贪心算法的基本思路可以总结为以下几步:
需要注意的是,贪心算法并不适用于所有的问题,因为并非所有问题都具有最优子结构性质。在某些情况下,贪心算法得到的结果可能并不是全局最优解,而只是一个较好的解。因此,在应用贪心算法时,需要仔细分析问题的特性,以确定贪心算法是否适用于该问题。
下面会介绍一些用贪心解决的算法题:
分析:
思路:
ret
负责记录统计过的当前最大利润,以及一个minPrev
用于记录最小的买入价格代码:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ret = 0;
for(int i = 0, minPrev = INT_MAX; i < prices.size(); ++i)
{
ret = max(ret, prices[i] - minPrev); // 更新结果
minPrev = min(minPrev, prices[i]);
}
return ret;
}
};
分析:
思路:
int i
遍历数组,当找到一处递增时,用指针j将该递增区间统计,当走出递增时,将i和j间的距离就是这一段的递增值(利润)代码:
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 利用天计算
int ret = 0, n = prices.size();
for(int i = 1; i < n; ++i)
{
if(prices[i] > prices[i-1])
ret += prices[i] - prices[i-1];
}
return ret;
}
};
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 利用双指针
int ret = 0, n = prices.size();
for(int i = 0; i < n; ++i)
{
int j = i;
while(j + 1 < n && prices[j+1] > prices[j])
j++;
ret += prices[j] - prices[i];
i = j; // 更新i到j+1
}
return ret;
}
};
分析:
思路:
代码:
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
int n = nums.size(), minElem = INT_MAX;
int m = 0;
for(auto x : nums) // 统计负数个数m
{
if(x < 0) ++m;
minElem = min(minElem, abs(x)); // 找数组中绝对值最小的数
}
int ret = 0;
if(m > k)
{
sort(nums.begin(), nums.end());
// 将前k小的负数转正,并统计到ret
for(int i = 0; i < k; ++i)
ret += abs(nums[i]);
for(int i = k; i < n; ++i)
ret += nums[i];
}
else // m == k: 将所有负数转正
{
// m < k:
// 先将所有负数转正;后根据k-m的奇偶性进行编写
for(auto x : nums)
ret += abs(x);
if((k - m) % 2) // 奇数 // 将当前数组的绝对值最小逆号
ret -= minElem * 2;
}
return ret;
}
};
分析:
vector
以及 pair
二元组来完成;也可以创建一个下标数组,根据身高对下标数组进行排序,最后直接输出names[index[i]]
;思路:
vector> people
,再将身高信息和姓名信息存放到该people进行降序排序names[index[i]]
代码:
class Solution {
public:
vector<string> sortPeople(vector<string>& names, vector<int>& heights) {
// 利用二元组
int n = names.size();
vector<pair<int, string>> people;
for (int i = 0; i < n; ++i) {
people.push_back(make_pair(heights[i], names[i]));
}
// 根据身高降序
sort(people.rbegin(), people.rend());
// 提取结果
vector<string> ret;
for (const auto& p : people) {
ret.push_back(p.second);
}
return ret;
}
};
class Solution {
public:
vector<string> sortPeople(vector<string>& names, vector<int>& heights) {
// 根据身高对下标进行排序:
// 创建下标数组
int n = heights.size();
vector<int> index(n);
for(int i = 0; i < n; ++i)
index[i] = i;
// 排序数组
sort(index.begin(), index.end(), [&](const int i, const int j){
return heights[i] > heights[j];
});
vector<string> ret;
// 提取结果到数组
for(int i = 0; i < n; ++i)
{
ret.push_back(names[index[i]]);
}
return ret;
}
};
分析:
思路:
代码:
class Solution {
public:
vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {
// 1. 排序数组
int n = nums1.size();
sort(nums1.begin(), nums1.end());
// 1.5 创建下标数组
vector<int> index2(n);
for(int i = 0; i < n; ++i)
index2[i] = i;
sort(index2.begin(), index2.end(), [&](const int i, const int j){
return nums2[i] < nums2[j];
});
// 2. 田忌赛马: 如果当前比得过,则插入当前位置
// 如果比不过,则匹配对方最大的
int left = 0, right = n-1;
vector<int> ret(n);
for(auto x : nums1)
{
if(x > nums2[index2[left]]) ret[index2[left++]] = x;
else ret[index2[right--]] = x;
}
return ret;
}
};