最优前缀编码 哈夫曼算法

1. 问题

  • 给定字符集 C = < x 1 , x 2 , . . . , x n > C= C=<x1,x2,...,xn> 和每个字符的频率 f ( x i ) f(x_i) f(xi),求关于 C C C 的一个最优前缀码。

2. 解析

  • 构造最优前缀码的贪心算法就是哈夫曼算法

  • p o p : pop: pop: 每次从队列中取出两个权值最小的节点当作孩子节点

  • p u s h : push: push 根据 p o p pop pop 操作取出的两个子结点,构成一个带有新的权值(两个子结点权值相加)的父结点后重新放入队列。

  • 重复 n − 1 n-1 n1 p o p pop pop p u s h push push 操作后,就构成了一颗哈夫曼树。

  • 这里设定右节点的边值为1,左节点的边值为0,那么从祖先节点到某个叶子节点的边集集合就是其最优前缀编码。
    最优前缀编码 哈夫曼算法_第1张图片
    最优前缀编码 哈夫曼算法_第2张图片

3. 设计

最优前缀编码问题(哈夫曼算法)

#include
const int N = 1e3+10;
using namespace std;



struct node {
	int val,id;
	bool friend operator<(const node &x,const node &y) {
		//按照键值降序排序
		if (x.val!=y.val) return x.val>y.val;
		return x.id>y.id;
	}
};

struct tree {
	int l,r,val;
}t[N];

string s;
priority_queue<node>q;
int n,m,k;
int fa[N];
string HafCode[N];

/**
 * 从叶子节点开始跳到祖先节点
 */
void getHafCode() {
	for (int i=1;i<=n;i++) {
		string c;
		int j = i;
		while (fa[j]) {
			c = (j==t[fa[j]].l?'0':'1')+c;
			j = fa[j];
		}
		HafCode[i] = c;
	}
}

void Haf() {
	/**
	 * 哈夫曼算法最多执行n-1次
	 */
	for (int i=1;i<n;i++) {
		//每次从堆中取出2个值最小的节点
		node a = q.top();
		q.pop();
		node b = q.top();
		q.pop();

		int id = i+n;
		int val = a.val+b.val;

		//建立树结构
		t[id].l = a.id;
		t[id].r = b.id;
		t[id].val = val;
		fa[a.id] = id;
		fa[b.id] = id;
		q.push({val,id});
	}
	getHafCode();
}

void run() {
	cin>>s;
	n = s.length();
	for (int i=1;i<=n;i++) {
		int val;
		cin>>val;
		q.push({val,i});
	}
	Haf();
	for (int i=1;i<=n;i++) {
		cout<<s[i-1]<<":";
		cout<<HafCode[i]<<endl;
	}
}

int main() {
	run();
	return 0;
}

4. 分析

  • 时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)

5. 源码

https://github.com/a894985555/Algorithm/tree/main/%E6%9C%80%E4%BC%98%E5%89%8D%E7%BC%80%E7%BC%96%E7%A0%81

你可能感兴趣的:(算法,算法)