贪心
贪心算法(Greedy Algorithms)是 C++ 等编程语言中常用的一种算法策略。
定义
贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解,如单源最短路径问题、最小生成树问题等。
特点
- 局部最优选择:每一步都做出当时看起来最佳的选择,它并不从整体最优的角度去考虑问题,而是只关注眼前的利益。
- 无后效性:即某个状态以前的过程不会影响以后的状态,只与当前状态有关。这使得算法在每一步的决策都相对简单,只依赖于当前的信息。
一般步骤
- 分解问题:把求解的问题分成若干个子问题。
- 局部最优决策:对每个子问题求解,得到子问题的局部最优解。
- 合并解:把子问题的局部最优解合成原来问题的一个解。
示例:活动选择问题(用 C++ 实现)
假设有一系列活动,每个活动都有开始时间和结束时间,目标是选择出最多的活动,使得这些活动之间没有时间冲突。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 定义活动结构体
struct Activity {
int start;
int end;
};
// 比较函数,用于按活动结束时间排序
bool compareEndTime(Activity a, Activity b) {
return a.end < b.end;
}
// 贪心算法实现活动选择
int activitySelection(vector<Activity> activities) {
// 按活动结束时间排序
sort(activities.begin(), activities.end(), compareEndTime);
int count = 1; // 选择的活动数量,初始选第一个活动
int lastEnd = activities[0].end; // 记录最后一个被选择活动的结束时间
for (int i = 1; i < activities.size(); i++) {
if (activities[i].start >= lastEnd) { // 如果当前活动开始时间晚于上一个活动结束时间
count++;
lastEnd = activities[i].end;
}
}
return count;
}
你可以使用以下方式调用这个函数:
int main() {
vector<Activity> activities = { {1, 4}, {3, 5}, {0, 6}, {5, 7}, {3, 8}, {5, 9}, {6, 10}, {8, 11}, {8, 12}, {2, 13}, {12, 14} };
int result = activitySelection(activities);
cout << "最多可以选择的活动数量: " << result << endl;
return 0;
}
上述代码中,compareEndTime
函数用于对活动按结束时间进行排序,activitySelection
函数实现了贪心算法,通过不断选择结束时间最早且与已选活动无冲突的活动,最终得到最多可选择的活动数量。
实例:哈夫曼编码问题
哈夫曼编码是一种用于数据压缩的算法,贪心算法可以很好地应用于构建哈夫曼树来实现哈夫曼编码。以下从原理、步骤和 C++ 代码示例等方面进行讲解:
原理
哈夫曼编码基于字符出现的频率,频率越高的字符被赋予越短的编码,频率越低的字符则被赋予越长的编码,从而达到数据压缩的目的。贪心算法在构建哈夫曼树的过程中,每次都选择频率最小的两个节点来构建新的父节点,直到所有节点合并成一棵完整的哈夫曼树。
步骤
-
统计字符频率:遍历待编码的数据,统计每个字符出现的频率。
-
创建节点:为每个字符创建一个节点,节点包含字符本身和其频率。
-
构建哈夫曼树
:
- 将所有节点放入一个优先队列(通常按频率升序排列)。
- 从优先队列中取出频率最小的两个节点,创建一个新的父节点,其频率为这两个节点频率之和。
- 将新的父节点重新放入优先队列。
- 重复上述步骤,直到优先队列中只剩下一个节点,该节点即为哈夫曼树的根节点。
-
生成编码:从哈夫曼树的根节点开始,通过遍历左右子树为每个字符生成对应的编码(通常左子树对应编码为 0,右子树对应编码为 1)。
-
编码数据:根据生成的编码表,对待编码的数据进行编码。
C++ 代码示例
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <string>
// 哈夫曼树节点结构体
struct HuffmanNode {
char data;
int frequency;
HuffmanNode* left;
HuffmanNode* right;
HuffmanNode(char c, int f) : data(c), frequency(f), left(nullptr), right(nullptr) {}
};
// 比较函数,用于优先队列按频率升序排列
struct Compare {
bool operator()(HuffmanNode* a, HuffmanNode* b) {
return a->frequency > b->frequency;
}
};
// 构建哈夫曼树
HuffmanNode* buildHuffmanTree(const std::unordered_map<char, int>& frequencyMap) {
std::priority_queue<HuffmanNode*, std::vector<HuffmanNode*>, Compare> pq;
for (const auto& pair : frequencyMap) {
pq.push(new HuffmanNode(pair.first, pair.second));
}
while (pq.size() > 1) {
HuffmanNode* left = pq.top();
pq.pop();
HuffmanNode* right = pq.top();
pq.pop();
HuffmanNode* parent = new HuffmanNode('\0', left->frequency + right->frequency);
parent->left = left;
parent->right = right;
pq.push(parent);
}
return pq.top();
}
// 生成哈夫曼编码表
void generateCodes(HuffmanNode* root, const std::string& code, std::unordered_map<char, std::string>& codeMap) {
if (root == nullptr) {
return;
}
if (root->data != '\0') {
codeMap[root->data] = code;
}
generateCodes(root->left, code + "0", codeMap);
generateCodes(root->right, code + "1", codeMap);
}
// 对数据进行编码
std::string encodeData(const std::string& data, const std::unordered_map<char, std::string>& codeMap) {
std::string encodedData;
for (char c : data) {
encodedData += codeMap[c];
}
return encodedData;
}
int main() {
std::string data = "hello world";
std::unordered_map<char, int> frequencyMap;
for (char c : data) {
frequencyMap[c]++;
}
HuffmanNode* root = buildHuffmanTree(frequencyMap);
std::unordered_map<char, std::string> codeMap;
generateCodes(root, "", codeMap);
std::string encodedData = encodeData(data, codeMap);
std::cout << "原始数据: " << data << std::endl;
std::cout << "哈夫曼编码: " << encodedData << std::endl;
return 0;
}
代码说明
- 节点结构体:定义了
HuffmanNode
结构体来表示哈夫曼树的节点,包含字符、频率以及左右子节点指针。 - 构建哈夫曼树:
buildHuffmanTree
函数根据字符频率构建哈夫曼树,使用优先队列来选择频率最小的节点。 - 生成编码表:
generateCodes
函数通过遍历哈夫曼树为每个字符生成对应的编码,并存储在codeMap
中。 - 编码数据:
encodeData
函数根据生成的编码表对待编码的数据进行编码。
通过上述步骤和代码,就可以使用贪心算法解决哈夫曼编码问题,实现对数据的压缩编码。