018-Huffman树-贪心-《算法设计技巧与分析》M.H.A学习笔记

Huffman是完全二叉树,权重较大的节点距离根较近。

Huffman编码是一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字。

基本思路:

建立Huffman树的过程:

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。
n个权值分别设为
w1、w2、…、wn,则哈夫曼树的构造规则为:

(1) 将w1、w2、…,wn看成是有n
棵树的森林(每棵树仅有一个结点);

(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;

(3)从森林中删除选取的两棵树,并将新树加入森林;

(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

选取权值最小的结点时我们用最小堆。

需要得出各个字母的具体编码,我们只需要在所有的左儿子的边标上0,右儿子的边标上1,从根节点到目标节点的所有经过的边的码就是该字母的编码。

算法分析:

假设有n个字符。

把所有字符插入堆需要Θ(n),从堆中删除两个元素和新加一个元素需要O(log n)。重复n-1次,所以总的时间复杂度是O(n
log n)。

伪代码:

C++代码:

//计算哈夫曼编码下的文本占的位数,并与定长编码的比较。
#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<queue>                 //哈夫曼树,用优先队列实现
using namespace std;
int main()
{
	string s;
	while (cin >> s)
	{
		if (s == "END") break;
		int len = s.size();

		int date[30] = { 0 };         //date数组记录text中各个字符的频数
		priority_queue<int>q;

		for (int i = 0; i < len; i++)
		{
			if (s[i] == '_') date[0]++;
			else date[s[i] - 'A' + 1]++;
		}

		for (int i = 0; i < 27; i++)
		{
			if (date[i]!=0) q.push(-date[i]);      //只把不同字符的频数加入优先队列,字符本身与题目要求无关
		}                                           //处理使小的数据的优先级别高

		int ans = 0;
		int tem;
		while (!q.empty())
		{
			tem = -q.top();     //取出最小的两个数,相加累计到ans中,并加入队列,一直处理到队列中没有数
			q.pop();
			if (!q.empty())
			{
				tem = tem - q.top();
				q.pop();
			}
			ans = ans + tem;
			if (!q.empty())
				q.push(-tem); //若队列已没有数据,则不添加(上面已经取出最后两个,或一个),若没有这一步,上面whlie的判断不成立。
		}                     

		int ans8 = len << 3;
		double bi = (double)ans8 / ans;

		printf("%d %d %.1lf\n", ans8, ans, bi);
	}
}
时间: 2024-10-13 11:42:45

018-Huffman树-贪心-《算法设计技巧与分析》M.H.A学习笔记的相关文章

016-kruskal算法-贪心-《算法设计技巧与分析》M.H.A学习笔记

最小生成树: 在一给定的连通无向图G = (V, E)中,(u, v) 代表连接顶点u与顶点v的边,而 w(u, v)代表此边的权重,若存在T为G的子集且为无循环图,使得w(T) 最小,则此T为G的最小生成树. 基本思路: kruskal算法总共选择n- 1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中.注意到所选取的边若产生环路则不可能形成一棵生成树.kruskal算法分e 步,其中e 是网络中边的数目.按耗费递增的顺序来考虑这e 条边,每次

017-Prim算法-贪心-《算法设计技巧与分析》M.H.A学习笔记

基本思路: 定义结点集合U, V (U表示已经选择加入MST的结点集合,V表示未选) 1. 任选一个结点加入U 2. 选择一条边权最小的边,他的两个结点分别属于U, V,并把属于V的那个结点加入U 3. 重复执行2直到V空 伪代码: C++代码: int g[mnx][mnx]; int n, m; int d[mnx]; // 朴素 prim, 复杂度O(|V|^2) |V|:点数, |E|:边数 int prim() { memset(d, 0x3f, sizeof d); //初始化 in

014-背包问题-动态规划-《算法设计技巧与分析》M.H.A学习笔记

01背包: 01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2--Wn,与之相对应的价值为P1,P2--Pn.求能获得的最大总价值. 基本思路: V[i,j]表示从前i件物品中取出的,能装入体积为j的背包的物品的最大总价值. 初始化条件: V[i,0]和V[0,j]都为0,我们从其意义上就可以理解. 状态转移方程: V[i,j]=max{ V[i-1,j],V[i-1,j-Wi]+Pi } ,前后分别为第i件物品不取和取得情况. 总的就是下面的递推式: 算法分析: 表

013--Floyd算法-动态规划-《算法设计技巧与分析》M.H.A学习笔记

多源最短路:有向图,求从每个顶点到其他所有顶点的最短距离. 基本思路: 假设有向图的所有点编号为1到n,l[i,j]表示从i到j的边的长度,如果不存在边,则置为正无穷. 定义d(k,i,j)表示从点i到点j,并且不经过编号大于k的点的最短距离. 初始化条件: K=0时,d(0,i,j)=l[i,j]. 状态转移方程: d(k,i,j)=min{ d(k-1,i,j),d(k-1,i,k)+d(k-1,k,j) }   1<=k<=n 于是我们有如下的递归式: 算法分析: 显然,Floyd算法的

算法设计技巧与分析笔记 第一章

1.搜索:设A[1--n]为一个n个元素的数组,判定给定元素x是否在A中 线性搜索:直接扫描A中所有项目,将每个项目与x做比较. 二分搜索: A[low--high]为有序非空数组(假定为升序),A[mid]为中间元素 假定x>A[mid],则丢弃A[low-mid],继续搜索A[mid+1-high]: 假定x<A[mid],则丢弃A[mid-high],继续搜索A[low-mid-1]: 若x=A[mid],返回mid,结束搜索. 算法分析:时间复杂度:O(log n) 2.排序:设A[1

019-dfs.bfs-图的遍历-《算法设计技巧与分析》M.H.A学习笔记

深度优先搜索DFS 深搜框架: bool dfs(int loc) { 标记状态loc已访问; if (loc为目标状态) return true; for (每个可能的操作) { 对loc应用操作产生新状态nstat; if (nstat合法且未被访问) { if (dfs(nstat)) return true; } } 撤销loc已访问标记; // 这步要具体问题具体分析了 return false; } 广度优先搜索BFS 实现方法 1. 首先将根节点放入队列中. 2. 从队列中取出第一

《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Alpha-Beta剪枝

2014.07.08 22:43 简介: “搜索”与“剪枝”几乎是如影随形的.此处的“搜索”指的是带有回溯算法的深度优先搜索. 在之前的“Minimax策略”中我们给出了一个三连棋的程序,运行后你就知道计算一步棋要花多少时间. 为了计算最优的一步棋,我们可能需要递归9万多次.如果毫无疑问这种阶乘式的穷举过程必须通过剪枝来加速. 本篇介绍一种用于Minimax策略的剪枝思路——α-β剪枝. 剪枝的英语是pruning,所以不要想当然说成trimming. 图示: 在上一篇讲解Minimax策略的博

五种常用的算法设计技巧之二:分治算法

一,介绍 分治算法主要包含两个步骤:分.治.分,就是递归地将原问题分解成小问题:治则是:在解决了各个小问题之后(各个击破之后)合并小问题的解,从而得到整个问题的解 二,分治递归表达式 分治算法一般都可以写出一个递归表达式:比如经典的归并排序的递归表达式:T(N)=2T(N/2)+O(N) T(N)代表整个原问题,采用了分治解决方案后,它可以表示成: ①分解成了两个规模只有原来一半(N/2)的子问题:T(N/2) ②当解决完这两个子问题T(N/2)之后,再合并这两个子问题需要的代价是 O(N) 递

【NOI2015】荷马史诗[Huffman树+贪心]

#130. [NOI2015]荷马史诗 统计 描述 提交 自定义测试 追逐影子的人,自己就是影子. ——荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由<奥德赛>和<伊利亚特>组成的鸿篇巨制<荷马史诗>实在是太长了,Allison 想通过一种编码方式使得它变得短一些. 一部<荷马史诗>中有 nn 种不同的单词,从 11 到 nn 进行编号.其中第 ii 种单词出现的总