贪心算法(求解最优化问题)及其应用赫夫曼编码

1、贪心算法求解流程:
通常自顶向下设计:做出一个选择(做局部最优选择),然后求解剩下的那个与原问题类似的子问题。
在这里插入图片描述
2、贪心算法设计步骤:
在这里插入图片描述
3、0-1背包问题与分数背包问题:
0-1背包问题:【动态规划】01背包问题(通俗易懂,超基础讲解)
分数背包问题:贪心算法,见算法导论16.2节

4、赫夫曼编码: 目标:字符出现频率高,使其二进制位数少
对于字符等数据,根据其出现频率,通过赫夫曼贪心算法构造出字符的最优前缀码(二进制表示),从而降低所占空间。
前缀码:没有任何码字是其他码字的前缀。
最优前缀码对应的树有N个叶节点(字符个数),且恰有N-1个内部节点。

4.1、赫夫曼二进制编码构造流程:
a)算法自底向上构造对应最优编码的二叉树T,从N个叶节点开始,执行N-1次合并操作创建最终二叉树。
b)算法使用字符出现频率为关键字的最小优先队列Q,以识别两个最低频率的对象将其合并,
c)合并两对象时得到的新对象频率设置为两原对象频率之和。
流程图如下:
在这里插入图片描述
4.2、赫夫曼二进制编码构造C++代码:
假设给定字符及其出现频率组成结构的数组nodeArr

#include <queue>
struct node{
	int num; // 字符出现频率
	char c; //字符
	node* left;
	node* right;
	node(int x,char y):num(x),c(y),left(null),right(null){}
	node():num(0),c('0'),left(null),right(null){}
	bool operator< (const node& a){
		return num>a.num; //从小到大排列
	}
}
node* nodeArr[10]; // 假设已经给定,且存储在动态内存

priority_queue<node*> q; // 按字符出现频率由小到大排列
for(int i = 0;i<10;i++){
	q.push(nodeArr[i]);
}
for(int i = 0;i<9;i++){ // 10个节点9条边
	node* z = new node;
	node* tmp1 = q.front();
	q.pop();
	node* tmp2 = q.front();
	q.pop();
	z->left = tmp1;
	z->right = tmp2;
	z->num = tmp1->num +tmp2->num;
	q.push(z);
}
node* root =  q.front(); //树的根节点

总结:

1、在构造哈夫曼编码过程中,频率低的放左节点,高的放右节点。
2、哈夫曼编码通过将出现频率高的字符用少数二进制位表示,从而降低使用空间,实现压缩编码。

赫夫曼编码是一种无损数据压缩算法,它是通过将出现频率高的字符用较短的编码表示,出现频率低的字符用较长的编码表示来进行压缩的。而贪心算法则是赫夫曼编码中用于构建哈夫曼树的主要算法。 贪心算法的思路是,每次从剩余的未编码字符中找到出现频率最小的两个字符进行合并,并将它们作为一个新的子节点插入到哈夫曼树中。这样,每次合并都能保证当前合并后的节点权值最小,从而最终构建出一棵最优的哈夫曼树。 以下是C++实现赫夫曼编码问题贪心算法的示例代码: ```c++ #include <iostream> #include <queue> #include <vector> using namespace std; // 定义哈夫曼树节点结构体 struct Node { char ch; // 节点代表的字符 int freq; // 字符出现的频率 Node *left, *right; // 左右子节点指针 Node(char ch, int freq) : ch(ch), freq(freq), left(NULL), right(NULL) {} }; // 定义优先队列比较函数 struct cmp { bool operator()(Node* a, Node* b) { return a->freq > b->freq; } }; // 构建哈夫曼树并返回根节点指针 Node* buildHuffmanTree(vector<pair<char, int>>& freq) { // 将每个字符及其出现频率转换为哈夫曼树节点,并加入优先队列中 priority_queue<Node*, vector<Node*>, cmp> pq; for (auto p : freq) { pq.push(new Node(p.first, p.second)); } // 依次取出队列中最小的两个节点进行合并,直到队列只剩下一个节点为止 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); parent->left = left; parent->right = right; pq.push(parent); } return pq.top(); } // 递归遍历哈夫曼树,生成编码表 void generateCodeTable(Node* root, string code, vector<pair<char, string>>& codeTable) { if (!root) return; if (root->ch != '\0') { codeTable.push_back(make_pair(root->ch, code)); } generateCodeTable(root->left, code + "0", codeTable); generateCodeTable(root->right, code + "1", codeTable); } // 主函数 int main() { string s = "hello world"; vector<pair<char, int>> freq(256, make_pair('\0', 0)); for (char c : s) { freq[c].first = c; freq[c].second++; } // 构建哈夫曼树 Node* root = buildHuffmanTree(freq); // 生成编码表 vector<pair<char, string>> codeTable; generateCodeTable(root, "", codeTable); // 输出编码表 for (auto p : codeTable) { cout << p.first << ": " << p.second << endl; } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值