一、贪心算法基础
贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法策略。其核心特点是:
局部最优选择:不考虑长远影响
无后效性:当前决策不影响之前状态
高效性:通常时间复杂度较低
// 经典贪心问题:找零钱
vector<int> greedyChange(int amount, vector<int>& coins) {
sort(coins.rbegin(), coins.rend()); // 降序排序
vector<int> result;
for(int coin : coins){
while(amount >= coin){
amount -= coin;
result.push_back(coin);
}
}
return amount == 0 ? result : vector<int>();
}
二、典型问题解析
1. 区间调度问题
struct Interval {
int start, end;
};
int intervalSchedule(vector<Interval>& intervals) {
sort(intervals.begin(), intervals.end(),
[](auto& a, auto& b){ return a.end < b.end; });
int count = 0, end = INT_MIN;
for(auto& inv : intervals){
if(inv.start >= end){
count++;
end = inv.end;
}
}
return count;
}
2. 霍夫曼编码
struct Node {
char ch;
int freq;
Node *left, *right;
};
struct comp {
bool operator()(Node* l, Node* r){
return l->freq > r->freq;
}
};
Node* buildHuffmanTree(string text) {
// 统计频率
unordered_map<char, int> freq;
for(char c : text) freq[c]++;
// 优先队列
priority_queue<Node*, vector<Node*>, comp> pq;
for(auto& p : freq)
pq.push(new Node{p.first, p.second, nullptr, nullptr});
// 构建树
while(pq.size() > 1){
Node* left = pq.top(); pq.pop();
Node* right = pq.top(); pq.pop();
Node* parent = new Node{'\0', left->freq+right->freq, left, right};
pq.push(parent);
}
return pq.top();
}
三、贪心算法的正确性证明
贪心选择性质:证明局部最优能导致全局最优
最优子结构:问题的最优解包含子问题的最优解
常用证明方法:
数学归纳法
交换论证法
决策树分析法
四、C++实现技巧
优先队列的使用:
priority_queue<int, vector<int>, greater<int>> minHeap;
自定义排序:
sort(items.begin(), items.end(), [](auto& a, auto& b){
return a.value/(double)a.weight > b.value/(double)b.weight;
});
STL算法组合:
auto it = max_element(begin, end, comp);
五、贪心算法的局限性
不适用于需要全局考虑的问题
可能陷入局部最优而非全局最优
典型反例:0-1背包问题
六、实战练习建议
LeetCode经典题目:
分发饼干
买卖股票的最佳时机II
无重叠区间
调试技巧:
#define DEBUG
#ifdef DEBUG
#define debug(x) cout << #x << " = " << x << endl
#else
#define debug(x)
#endif
七、进阶思考
贪心算法与动态规划的关系
拟阵理论与贪心算法
近似算法中的贪心策略