第十章贪婪算法(哈夫曼编码)

贪婪算法:贪婪算法分阶段地工作,在每一个阶段,可以认为所作决定是好的,而不考虑将来的后果。这意味着选择的是某个局部的最优。当算法终止时,我们希望局部最优就是全局最优。这样的话,算法就是正确的。
否则的话,算法得到的就是一个次最优解。
Dijkstra算法,prim算法,Kruskal算法,都是贪婪算法。

对字符编码:代表字母的二进制编码可以用二叉树来表示,在树中,只有树叶有数据。每个字符通过从根节点开始用0表示左分支
用1表示右分支而记录路径的方法表示出来。只要字符都存放到树叶上,那么任何比特序列总能被毫无歧义的译码。

哈夫曼编码:是字符编码的一种方式,是一种不等长的编码,频率出现高的字符,编码长度小;编码无二义性,无前缀码,任何一个字符的编码不能是另一个字符的前缀

哈夫曼算法描述:哈夫曼算法是对字符编码的一种算法。(假设字符的个数为N个)算法是对一个树组成的森林进行的。一棵树的权等于它的树叶的频率的和。任意选取最小权的两颗树T1和T2,并任意形成以T1和T2为子树的新树,新树的权值为T1+T2的权值。这样进行N-1次。就得到了一棵哈夫曼树,这棵树就是最优哈夫曼编码树。

求解步骤
1.确定数据结构
一棵有n个叶子结点的哈夫曼树共有2n - 1个结点(n-1次合并,每次产生一个新结点)
构成哈夫曼树后,从根节点到叶子节点的路径构成哈夫曼编码
2. 初始化。构造n棵结点为n个字符的单结点树的集合T = {t1,t2,t3,t4…tn};
3. 每次从结点树的集合中找出权值最小的两个结点,合并成为新的结点,加入树的集合,直到树集合中只剩下一棵树,求解哈夫曼树完成。
4.根据哈夫曼树求解哈夫曼编码,约定树的左分支为 0 ,树的右分支为 1;从根结点到叶子结点的路径完成哈夫曼编码

#include 
using namespace std;
#define MAXBIT 30
// 哈夫曼树的结点的结构类型

typedef struct  HuNodeType * HuNode;
struct HuNodeType
{
	double weight;		//权值
	HuNode  parent;		//父亲结点
	HuNode Lchild;		//左孩子
	HuNode  Rchild;		//右孩子
	char value;			//结点表示的字符
};

typedef struct HuCodeType* HuCode;
struct HuCodeType
{
	int bits[MAXBIT];	// 存储哈夫曼编码的数组、
	int start;			// 编码开始的下标
};

//链表存储树的集合
typedef struct HuffList * HuList;
struct HuffList
{
	HuNode Node;		//存储树节点
	HuList Next;		
	HuList Parent;
};

HuNode HuffmanTree(int n) 		// 求解哈夫曼树
{
	// 初始化链表,存储森林中树的根节点
	HuList HuffList = (HuList)malloc(sizeof(struct HuffList));
	HuffList->Next = NULL;
	HuffList->Node = NULL;
	HuList N, P = HuffList;
	HuNode tmp;

	//初始化字符节点,把字符节点存入链表,每一个字符节点都代表一棵子树
	for (int i = 0; i < n; i++)
	{
		tmp = (HuNode)malloc(sizeof(struct HuNodeType)); //新的树节点
		tmp->Lchild = NULL;
		tmp->Rchild = NULL;
		tmp->parent = NULL;
		cout << "请输入字符和权值" << endl;
		cin >> tmp->value >> tmp->weight;
		N = (HuList)malloc(sizeof(struct HuffList));	//链表节点,存储树根
		N->Node = tmp;	
		N->Next = NULL;
		P->Next = N;			//加入链表
		N->Parent = P;			
		P = N;
	}

	HuList X1, X2;
	//合并子树,形成新的树,加入链表
	while (HuffList->Next->Next != NULL)			//链表中多个子树,合并到链表中只要一棵子树
	{
		P = (HuList)malloc(sizeof(struct HuffList));
		tmp = (HuNode)malloc(sizeof(struct HuNodeType));
		tmp->weight = 10000;						
		N = HuffList->Next;	
		//N为链表的头节点
		P->Node = tmp;
		X1 = P;
		X2 = P;
		for (N; N != NULL; N = N->Next) // 找出权值最小的两个结点
		{
			//x1存放的节点是权值最小的节点,x2存放的节点是权值次小的节点
			if (N->Node->weight < X1->Node->weight)
			{
				X2 = X1;
				X1 = N;
			}
			else if (N->Node->weight < X2->Node->weight)
			{
				X2 = N;
			}
		}
		//合并两个子树,tmp为新的树的头节点
		tmp->weight = X1->Node->weight + X2->Node->weight;
		tmp->Lchild = X1->Node;
		tmp->Rchild = X2->Node;
		X1->Node->parent = tmp;
		X2->Node->parent = tmp;
		P->Node = tmp;
		//将P节点加入链表中
		P->Parent = HuffList;
		P->Next = HuffList->Next;
		HuffList->Next->Parent = P;
		HuffList->Next = P;
		// 将X1,X2;从链表中删除
		if (X1->Next == X2)
		{
			X1->Parent->Next = X2->Next;
			if (X2->Next != NULL)
				X2->Next->Parent = X1->Parent;
		}
		else
		{
			X1->Parent->Next = X1->Next;
			if (X1->Next != NULL)
				X1->Next->Parent = X1->Parent;
		}
		if (X2->Next == X1)
		{
			X2->Parent->Next = X1->Next;
			if (X1->Next != NULL)
				X1->Next->Parent = X2->Parent;
		}
		else
		{
			X2->Parent->Next = X2->Next;
			if (X2->Next != NULL)
				X2->Next->Parent = X2->Parent;
		}
	}
	HuffList->Next->Node->parent = NULL;
	return HuffList->Next->Node;			//返回树根
}
void HuffmanCode(HuNode RootTree)			// 求解哈夫曼编码
{
	if (RootTree)			//根不为空
	{
		if (RootTree->Lchild == NULL && RootTree->Rchild == NULL)	//访问到树叶节点,即字符节点
		{	
			printf("%c: ", RootTree->value);  //第一次遇到节点的时候访问;
			HuNode R = RootTree;
			int Bit[MAXBIT];
			int S = 0;
			while (R->parent != NULL)
			{
				//求解哈夫曼编码
				if (R->parent->Lchild == R)
				{
					Bit[S] = 0;
				}
				else
				{
					Bit[S] = 1;
				}
				S++;
				R = R->parent;
			}
			S--;
			for (S; S >= 0; S--)
			{
				cout << Bit[S];
			}
			cout << endl;
		}
		HuffmanCode(RootTree->Lchild);
		HuffmanCode(RootTree->Rchild);
	}
}
int main()
{
	int n;
	cout << "请输入哈夫曼叶子节点的个数" << endl;
	cin >> n;
	HuNode RootTree = HuffmanTree(n);	// 返回树的根节点
	HuffmanCode(RootTree);
	return 0;
}

你可能感兴趣的:(数据结构与算法分析)