数据结构与算法分析-C++描述 第10章 算法设计技巧(贪心算法之霍夫曼编码)

算法设计技巧一 :贪心算法(Greedy Algorithm)

        在第9章曾多次遇到贪心算法的应用,如解决单源最短路径的Dijkstra算法,最小生成树的Prim算法,最小生成树的Kruskal算法。贪心算法分阶段进行。在每一阶段可以认为所做的决定是最好的,而不考虑将来的结果。一般来说,这意味着选择是某个局部优的。这种“眼下能够拿到的就拿”的策略即是这类算法名称的来源。当算法结束时,我们希望局部最优就是全局最优。如果真是这样,算法就是正确的;否则,得到的是一个次最优解。如果不要求绝对的最佳答案,那么使用简单的贪心算法生成近似答案。

        贪心算法的典型事例为找零钱问题(用最少数量的纸币凑出零钱总数)。更复杂的应用包括:非抢占式任务调度、霍夫曼编码、近似装箱问题、背包问题等。每一类问题又将产生相似的NP完整性问题,灵活多变,留下来极大地思考空间。

霍夫曼编码(Huffman Coding):

       使用贪心算法思维的霍夫曼编码算法是一个很好的压缩编码/解码算法,在各类压缩算法中得到广泛应用。其基本思想是出现最多频率的字符用最少的编码位,从而节省资源。具体算法如下:

        维护一个由树组成的森林。一棵树的权等于它的叶子的频率和。任意选取最小权的两棵树T1和T2,并任意形成以T1和T2为子树的新树,重复该过程C-1次(其中C为字符总数)。算法结束时的一棵树即为最优霍夫曼编码树。

行为描述(Action Description):

        对于一组字符[a, b, c,e, f],其对应的频率统计为][45, 13, 12, 16, 9, 5],对字符按照频率升序顺序排列,实现霍夫曼编码过程如下图(自下而上生成霍夫曼树):

       编码过程对应的霍夫曼树如下图:

霍夫曼编码算法编程实现:

//main.cpp
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<queue>
#include<algorithm>

using namespace std;

struct Tree{
	char key;
	int frequency;
	Tree *left;
	Tree *right;
	
	//Tree constructor
	Tree(char k = ' ', int f = 0, Tree *l = NULL, Tree *r = NULL):key(k), frequency(f), left(l), right(r){
	
	};
	
};

struct LargerThan{
	bool operator()(Tree *first, Tree *second){
		return first -> frequency > second -> frequency;
	}
};

priority_queue<Tree*, vector<Tree*>, LargerThan> priQueue;

//print the result of Huffman coding
void printCode(Tree *root, string str);

//delete the tree
void deleteTree(Tree *root);

//huffman algorithm
void huffman(vector<char> &kArray, vector<int> &fArray);

int main(){
	ifstream fin("input.txt");
	char k;
	int f;
	vector<char> kArray;
	vector<int> fArray;
	while(fin>>k){
		kArray.push_back(k);
		fin >> f;
		fArray.push_back(f);
	}
	fin.close();
	cout << "The info as follows : " << endl;
	for(int i = 0; i < kArray.size(); i++){
		cout << kArray[i] << " : " << fArray[i] << endl;
	}
	huffman(kArray, fArray);
	cout << "done ." << endl;
	return 0;
}


void huffman(vector<char> &kArray, vector<int> &fArray){
	//create sigle tree group as froest
	for(int i = 0; i < kArray.size(); i++){
		Tree *root = new Tree(kArray[i], fArray[i]);
		priQueue.push(root);
	}
	//merge the forest as sigle tree
	while(priQueue.size() > 1){
		Tree *l;
		Tree *r;
		l = priQueue.top();
		priQueue.pop();
		r = priQueue.top();
		priQueue.pop();
		Tree *root = new Tree('\0', l -> frequency + r -> frequency, l, r);
		priQueue.push(root);
	}
	string str = "";
	printCode(priQueue.top(), str);
	deleteTree(priQueue.top());
}

void deleteTree(Tree *root){
	if(root == NULL){
		return;
	}
	deleteTree(root -> left);
	deleteTree(root -> right);
	delete root;
}

void printCode(Tree *root, string str){
	if(root == NULL){
		return;
	}
	//assign the left child coding
	if(root -> left){
		str += '0';
	}
	printCode(root -> left, str);
	
	//print the leaf coding
	if(!root -> left && !root -> right){
		cout << root -> key << " 's Huffman code is : " << str << endl;
	}
	
	str.pop_back();
	//assign the right child coding
	if(root -> right){
		str += '1';
	}
	printCode(root -> right, str);
}

//Note: compile with : g++ -std=c++11 -o main main.cpp
//input.txt
a 45
b 13
c 12
d 16
e 9
f 5

 实验结果:

practice makes perfect !

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值