贪心算法在C++中的应用与实践
一、贪心算法基础
贪心算法(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
七、进阶思考
-
贪心算法与动态规划的关系
-
拟阵理论与贪心算法
-
近似算法中的贪心策略