Explicación detallada en lenguaje C del árbol de Huffman

Explicación detallada en lenguaje C del árbol de Huffman

1) Explicación de algunos términos:

  1. Ruta: en un árbol, la ruta de un nodo a otro se llama ruta. En la Figura 1, la ruta desde el nodo raíz al nodo a es una ruta.

  2. Longitud de la ruta: en una ruta, cada vez que pasa un nodo, la longitud de la ruta debe aumentarse en 1. Por ejemplo, en un árbol, el nodo raíz se especifica como una capa, luego la longitud de la ruta desde el nodo raíz hasta el nodo de la i-ésima capa es i-1. La longitud de la ruta desde el nodo raíz al nodo c en la Figura 1 es 3.

  3. Peso del nodo: asigne un nuevo valor a cada nodo, que se denomina peso del nodo.

  4. La longitud de la ruta ponderada del nodo: se refiere al producto de la longitud de la ruta desde el nodo raíz al nodo y el peso del nodo.

2) Definición de árbol de Huffman:

Al intentar construir un árbol con n nodos (todos como nodos hoja y cada uno con su propio peso), si la longitud de la ruta ponderada del árbol que se va a construir es la más pequeña, el árbol se denomina "árbol binario óptimo", A veces llamado "árbol de Huffman" o "árbol de Huffman".

Al construir el árbol de Huffman, para minimizar la longitud de la ruta ponderada del árbol, solo se debe seguir un principio, es decir: el nodo con el mayor peso está más cerca de la raíz del árbol. En la Figura 1, debido a que el nodo a tiene el mayor peso, debe usarse directamente como nodo hijo del nodo raíz.

3) Proceso de construcción:

Para n nodos dados con pesos respectivos, existe una forma efectiva de construir un árbol de Huffman:

  1. Se seleccionan dos pesos más pequeños de n pesos, y los dos nodos correspondientes forman un nuevo árbol binario, y el peso del nodo raíz del nuevo árbol binario es la suma de los pesos de los hijos izquierdo y derecho;

  2. Elimine los dos pesos más pequeños de los n pesos originales y agregue los nuevos pesos a los rangos de n – 2 pesos, y así sucesivamente;

  3. Repita 1 y 2 hasta que todos los nodos se construyan en un árbol binario, que es el árbol de Huffman.

4) Implementación del algoritmo:

Al construir el árbol de Huffman, es necesario filtrar los dos nodos con el valor más pequeño cada vez de acuerdo con el valor de peso de cada nodo, y luego construir un árbol binario.

La idea de encontrar los dos nodos con el valor de peso más pequeño es: comenzando desde la posición inicial del grupo de árboles, primero busque dos nodos sin nodos principales (lo que indica que no se han utilizado para construir un árbol) y luego siga el nodo sin padre Para comparar los nodos a su vez, hay dos situaciones a considerar:

  • Si es más pequeño que el más pequeño de los dos nodos, mantenga este nodo y elimine el nodo más grande original;

  • Si está entre dos valores de peso de nodo, reemplace el nodo más grande original;

#include<iostream>
#include<string.h>
#define  MAX 10000 
/*
请输入一段文字:this*program*is*my*favourite
字符和权值信息如下
字符:t  权值:2
字符:h  权值:1
字符:i  权值:3
字符:s  权值:2
字符:*  权值:4
字符:p  权值:1
字符:r  权值:3
字符:o  权值:2
字符:g  权值:1
字符:a  权值:2
字符:m  权值:2
字符:y  权值:1
字符:f  权值:1
字符:v  权值:1
字符:u  权值:1
字符:e  权值:1
********************************
字符编码为:
t:1000
h:11010
i:001
s:1001
*:011
p:11011
r:010
o:1010
g:11100
a:1011
m:1100
y:11101
f:11110
v:11111
u:0000
e:0001
文字编码为:
100011010001100101111011010101011100010101111000110011001011110011101011111101011111111010000001000110000001
********************************
译码:
请输入要译码的二进制字符串,输入'#'结束:100011010001100101111011010101011100010101111000110011001011110011101011111101011111111010000001000110000001#
译码为:
this*program*is*my*favourite
是否继续?(Y/N):
*/
using namespace std;
typedef struct {
	char letter, *code;
	int weight;
	int parent, lchild, rchild;
}HTNode, *HuffmanTree;
int n;
char coding[100];
int Min(HuffmanTree &HT, int i)
{
	int j;
	unsigned int k = MAX;
	int flag;
	for (j = 0; j <= i; ++j)
	{
		if (HT[j].weight < k && HT[j].parent == 0)//用父结点是否为0来判断此结点是否已经被选过  
		{
			k = HT[j].weight;
			flag = j;
		}
	}
	HT[flag].parent = 1;//作个标记,说明已经被选择了,因为在Select函数中要选择权值小的两个结点  
	return flag;
}
void Select(HuffmanTree &HT, int i, int &s1, int &s2)
{
	//在HT[1...i]中选择parent为0且权值最小的两个结点,其序号分别为s1,s2  
	//s1 <= s2  
	s1 = Min(HT, i);
	s2 = Min(HT, i);
}
void CreateHuffmanTree(HuffmanTree &HT, char t[], int w[])
{
	int m;
	int i, s1, s2;
	if (n <= 1)
		return;
	m = 2 * n - 1; //总共需要2n-1个节点
	HT = new HTNode[m + 1];//开辟空间
	for (i = 0; i<n; i++)
	{
		HT[i].code = '\0';
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].letter = t[i];
		HT[i].weight = w[i];
	}
	for (i = n; i <= m; i++)
	{
		HT[i].code = '\0';
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].letter = ' ';
		HT[i].weight = 0;
	}
	cout << "********************************" << endl;
	for (i = n; i<m; i++)
	{
		Select(HT, i - 1, s1, s2);//在n个数中找出权值最小的两个

		HT[s1].parent = i;
		HT[s2].parent = i;//将他们两个的parent节点设置为i;

		HT[i].lchild = s1;
		HT[i].rchild = s2;//把这两个分别当作左右节点
		HT[i].weight = HT[s1].weight + HT[s2].weight;//他们两个的双亲为他们两个的和;

	}
}
void CreatHuffmanCode(HuffmanTree HT)
{
	int start, c, f;
	int i;
	char *cd = new char[n];
	cd[n - 1] = '\0';
	cout << "字符编码为:" << endl;
	for (i = 0; i<n; i++)
	{
		start = n - 1;
		c = i;
		f = HT[i].parent;
		while (f != 0){
			--start;
			if (HT[f].lchild == c){
				cd[start] = '0';
			}
			else{
				cd[start] = '1';
			}
			c = f;
			f = HT[f].parent;
		}
		HT[i].code = new char[n - start];
		strcpy(HT[i].code, &cd[start]);
		cout << HT[i].letter << ":" << HT[i].code << endl;
	}
	delete cd;
}
void HuffmanTreeYima(HuffmanTree HT, char cod[], int b)           //译码
{
	char sen[100];
	char temp[50];
	char voidstr[] = " ";       //空白字符串
	int t = 0;
	int s = 0;
	int count = 0;
	for (int i = 0; i<b; i++)
	{
		temp[t++] = cod[i];     //读取字符
		temp[t] = '\0';        //有效字符串
		for (int j = 0; j<n; j++){        //依次与所有字符编码开始匹配
			if (!strcmp(HT[j].code, temp)){                //匹配成功
				sen[s] = HT[j].letter;    //将字符保存到sen中
				s++;
				count += t;
				strcpy(temp, voidstr);                //将TEMP置空 
				t = 0;          //t置空
				break;
			}
		}
	}
	if (t == 0){     //t如果被置空了,表示都匹配出来了,打印译码
		sen[s] = '\0';
		cout << "译码为:" << endl;
		cout << sen << endl;
	}
	else{                             //t如果没有被置空 , 源码无法被完全匹配
		cout << "二进制源码有错!从第" << count + 1 << "位开始" << endl;
	}
}
int main()
{
	HuffmanTree HT;
	char a[100], buff[1024], p;//a为存放字符 buff为输入的字符串 p为输入译码时的字符 
	int b[100];//存放权值信息 
	int i, j;
	int symbol = 1, x, k; //译码时做判断用的变量  
	cout << "请输入一段文字:";
	cin >> buff;
	int len = strlen(buff);
	for (i = 0; i<len; i++)
	{
		for (j = 0; j<n; j++)
		{
			if (a[j] == buff[i])
			{
				b[j] = b[j] + 1;
				break;
			}
		}
		if (j >= n)
		{
			a[n] = buff[i];
			b[n] = 1;
			n++;
		}
	}
	cout << "字符和权值信息如下" << endl;
	for (i = 0; i<n; i++)
	{
		cout << "字符:" << a[i] << "  权值:" << b[i] << endl;
	}
	CreateHuffmanTree(HT, a, b);
	CreatHuffmanCode(HT);
	cout << "文字编码为:\n";
	for (int i = 0; i < len; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (buff[i] == HT[j].letter)
			{
				cout << HT[j].code;
				break;
			}
		}
	}
	cout <<endl<< "********************************" << endl;
	cout << "译码:" << endl;
	while (1)
	{
		cout << "请输入要译码的二进制字符串,输入'#'结束:";
		x = 1;//判断是否有非法字符只能是0 1 
		k = 0;//作为循环变量来使coding【k】=输入的字符 
		symbol = 1;//判断是否输入结束 
		while (symbol){
			cin >> p;
			if (p != '1'&&p != '0'&&p != '#'){ //若存在其它字符,x设为0,表示输入的不是二进制
				x = 0;
			}
			coding[k] = p;
			if (p == '#')
				symbol = 0;  //#号结束标志
			k++;
		}
		if (x == 1){
			HuffmanTreeYima(HT, coding, k - 1);        //进行译码
		}
		else{
			cout << "有非法字符!" << endl;
		}
		cout << "是否继续?(Y/N):";
		cin >> p;
		if (p == 'y' || p == 'Y')
			continue;
		else
			break;
	}
	return 0;
}

 

Supongo que te gusta

Origin blog.csdn.net/m0_49019274/article/details/115290550
Recomendado
Clasificación