【数据结构】赫夫曼树与编码

前言

(概念)
路径:从一个节点到另一个节点的分支
路径长度:从一个节点到另一个节点的分支总数
节点的带权路径:该节点到根节点之间路径长度与权值的乘积
树的带权路径长度(WPL):所有叶子节点的带权路径长度之和
在这里插入图片描述

赫夫曼树

存储结构

赫夫曼树采取静态存储的方式,以下面为例:
在这里插入图片描述

初始化树

所需头文件:

#include<iostream>
using namespace std;
#include<stdlib.h>
#include<string.h>
#include<queue>

初始化树:

const int n = 8;                   //叶子个数(权值个数)
const int m = 2 * n;               //树节点个数+1(第0下标不放值,是标记位)
typedef unsigned int WeightType;   //权值类型
typedef unsigned int NodeType;     //节点类型

typedef struct       //树节点
{
	WeightType weight;
	NodeType parent, leftchild, rightchild;
}HtNode;

typedef HtNode HuffmanTree[m];    //赫夫曼树

void InitHuffmanTree(HuffmanTree hft,const WeightType w[])
{
	for (int i = 0; i < n; i++)
	{
		hft[i + 1].weight = w[i];
	}
}

打印树:

void PrintHuffmanTree(HuffmanTree hft)
{
	cout << "(赫夫曼树:)" << endl;
	for (int i = 1; i < m; i++)
	{
		cout << "  index: " << i << "\tweight: " << hft[i].weight << "\tparent: " << hft[i].parent << "\tLchild: " << hft[i].leftchild << "\tRchild: " << hft[i].rightchild << endl;
	}
	cout << endl << endl;
}

主函数:

int main()
{
	WeightType w[n] = { 5,29,7,8,14,23,3,11 };
	HuffmanTree hft = { 0 };
	InitHuffmanTree(hft, w);
	PrintHuffmanTree(hft);
	return 0;
}

初始化结果:
在这里插入图片描述

构建树

在这里插入图片描述

使用优先级队列来存放存有下标和权值的结构体,使用greater:将数据大的下沉(形成小顶堆),使用less:将小数据下沉(形成大顶堆)

代码实现:

struct IndexWeight
{
	int index;   //下标
	WeightType weight;   //权值
	operator WeightType() const { return weight; }   //重载(当用此结构体作比较时,用weight来进行比较)
};

void CreateHuffmanTree(HuffmanTree hft)
{
	std::priority_queue<IndexWeight, vector<IndexWeight>, std::greater<IndexWeight>> qu;     //优先级队列(小顶堆)
	for (int i = 1; i <= n; i++)
	{
		qu.push(IndexWeight{ i,hft[i].weight });
	}

	int k = n + 1;
	while (!qu.empty())
	{
		if (qu.empty()) { break; }
		IndexWeight left = qu.top();
		qu.pop();
		if (qu.empty()) { break; }
		IndexWeight right = qu.top();
		qu.pop();
		hft[k].weight = left.weight + right.weight;
		hft[k].leftchild = left.index;
		hft[k].rightchild = right.index;
		hft[left.index].parent = k;
		hft[right.index].parent = k;
		qu.push(IndexWeight{ k,hft[k].weight });
		k++;
	}
}

打印树:

void PrintHuffmanTree(HuffmanTree hft)
{
	cout << "(赫夫曼树:)" << endl;
	for (int i = 1; i < m; i++)
	{
		cout << "  index: " << i << "\tweight: " << hft[i].weight << "\tparent: " << hft[i].parent << "\tLchild: " << hft[i].leftchild << "\tRchild: " << hft[i].rightchild << endl;
	}
	cout << endl << endl;
}

主函数:

int main()
{
	WeightType w[n] = { 5,29,7,8,14,23,3,11 };
	HuffmanTree hft = { 0 };
	InitHuffmanTree(hft, w);
	CreateHuffmanTree(hft);
	PrintHuffmanTree(hft);
	return 0;
}

构建结果:
在这里插入图片描述

赫夫曼编码

以此赫夫曼树为例:
在这里插入图片描述

原本发送"ABCDEFGH"这些字符时,每个字符都是8个二进制位(char 1字节)总共要发:8*8=64位二进制,但通过赫夫曼编码后总共要发:27位二进制,因此赫夫曼编码在文件传输时有巨大作用

初始化编码

代码实现:

typedef struct
{
	char ch;        //节点对应的数据
	char code[n+1]; //该节点的赫夫曼编码
}HuffmanCode;

typedef HuffmanCode HuffmanCoding[n];

void InitHuffmanCode(HuffmanCoding hfc,const char ch[])
{
	for(int i=0;i<n;i++)
	{
		hfc[i].ch=ch[i];
		hfc[i].code[n]={'\0'};
	}
}

打印编码:

void PrinthuffmanCode(HuffmanCoding hfc)
{
	for (int i = 0; i < n; i++)
	{
		cout << " data: " << hfc[i].ch << " 的赫夫曼编码:" << hfc[i].code << endl;
	}
}

主函数:

int main()
{
	WeightType w[n] = { 5,29,7,8,14,23,3,11 };
	HuffmanTree hft = { 0 };
	InitHuffmanTree(hft, w);
	CreateHuffmanTree(hft);
	PrintHuffmanTree(hft);
	const char ch[n + 1] = { "ABCDEFGH" };  //必须是n+1,双引号最后会有一个'\0'
	HuffmanCoding hfc = { 0 };
	InitHuffmanCode(hfc, ch);
	PrinthuffmanCode(hfc);
	return 0;
}

初始化结果:
在这里插入图片描述

构建编码

从叶子节点向根节点走,如果是左孩子则为0,反之则为1
创建一个数组来存放0,1值,从后向前存放,这样读取的时候就是正着读取的

代码实现:

void CreateHuffmanCode(HuffmanTree hft, HuffmanCoding hfc)
{
	char code[n + 1] = { 0 };
	for (int i = 1; i <= 8; i++)
	{
		int k = n;
		int child = i;
		int parent = hft[i].parent;
		while (parent != 0)
		{
			if (child == hft[parent].leftchild)
			{
				code[--k] = { '0' };
			}
			else if (child == hft[parent].rightchild)
			{
				code[--k] = { '1' };
			}
			child = parent;
			parent = hft[child].parent;
		}
		strcpy_s(hfc[i - 1].code, n, &code[k]);
	}
}

打印编码:

void PrinthuffmanCode(HuffmanCoding hfc)
{
	for (int i = 0; i < n; i++)
	{
		cout << " data: " << hfc[i].ch << " 的赫夫曼编码:" << hfc[i].code << endl;
	}
}

主函数:

int main()
{
	WeightType w[n] = { 5,29,7,8,14,23,3,11 };
	HuffmanTree hft = { 0 };
	InitHuffmanTree(hft, w);
	CreateHuffmanTree(hft);
	PrintHuffmanTree(hft);

	const char ch[n + 1] = { "ABCDEFGH" };  //必须是n+1,双引号最后会有一个'\0'
	HuffmanCoding hfc = { 0 };
	InitHuffmanCode(hfc, ch);
	CreateHuffmanCode(hft, hfc);
	PrinthuffmanCode(hfc);
}

构建结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逐梦的白鹰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值