数据结构与算法(10)—— 哈夫曼树

哈夫曼树和哈夫曼编码

  • 算法的描述如下:
    1)将这N个结点分别作为N棵仅含一个结点的二叉树,构成森林F。
    2)构造一个新结点,并从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值
    置为左、右子树上根结点的权值之和。
    3)从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
    4)重复步骤2)和3),直至F中只剩下一棵树为止。
    在这里插入图片描述

哈夫曼树的特点

  • 没有度为 1 的结点
  • n 个叶结点的哈夫曼树共有 2n-1 个结点
  • 哈夫曼树的任意非叶结点的左右子树交换后仍是哈夫曼树
  • 对同一组权值,可能存在不同构的多棵哈夫曼树

最小堆实现哈夫曼树

刚开始挺懵逼,堆里面怎么还出哈夫曼树了呢…看姥姥留言堆里本来就是放东西的,既然可以放数…那为什么不能放树,醍醐灌顶

#include<iostream>
#include<malloc.h>
#define MaxSize 1000
#define MinData -1000 
int A[] = {1,3,5,8};  // 预先定义好一组权值 
int A_length = 4;  // 定义其长度 
typedef struct HeapStruct *MinHeap;   
typedef struct TreeNode *HuffmanTree;
struct HeapStruct{  // 存放哈夫曼树的堆 
	HuffmanTree *data;   // 存值的数组  
	int size;   // 堆的当前大小  
	int capacity; // 最大容量	
};
struct TreeNode{ // 哈夫曼树 
	int weight;  //权值
	HuffmanTree Left;  // 左子树 
	HuffmanTree right; // 右子树 
}; 
using namespace std;

MinHeap create(); // 初始化堆
HuffmanTree Create(); // 初始化哈夫曼树 
void sort(MinHeap H,int i); // 调整子最小堆 
void adjust(MinHeap H); // 调整最小堆 
void BuildMinHeap(MinHeap H);  // 建堆 
HuffmanTree Delete(MinHeap H); // 删除最小堆元素 
void Insert(MinHeap H,HuffmanTree Huff);  // 插入最小堆元素 
void PreOrderTraversal(HuffmanTree Huff); // 先序遍历 
HuffmanTree Huffman(MinHeap H); // 哈夫曼树的构建 

// 初始化堆
MinHeap create(){
	MinHeap H;
	HuffmanTree Huff;
	H = (MinHeap)malloc(sizeof(struct HeapStruct));
	H->data = (HuffmanTree *)malloc(sizeof(struct TreeNode) * (MaxSize+1));
	H->capacity = MaxSize;
	H->size = 0;
	// 给堆置哨兵 
	Huff = Create();
	Huff->weight = MinData;
	H->data[0] = Huff;
	return H;
} 

// 初始化哈夫曼树 
HuffmanTree Create(){
	HuffmanTree Huff;
	Huff = (HuffmanTree)malloc(sizeof(struct TreeNode));
	Huff->weight = 0;
	Huff->Left = NULL;
	Huff->right = NULL;
	return Huff;
}

// 调整子最小堆 
void sort(MinHeap H,int i){
	int parent,child;
	int tmp = H->data[i]->weight; // 取出当前"根结点"值
	for(parent=i;parent*2<=H->size;parent = child){
		child = 2 * parent;
		if((child!=H->size) && (H->data[child+1]->weight < H->data[child]->weight))
			child++;
		if(H->data[child]->weight >= tmp)
			break;
		else
			H->data[parent] = H->data[child];
	} 
	H->data[parent]->weight = tmp;
}

// 调整最小堆 
void adjust(MinHeap H){
	for(int i =H->size/2;i>0;i--)
		sort(H,i);// 每个"子最小堆"调整 
}

// 建堆 
void BuildMinHeap(MinHeap H){
	// 将权值读入堆中
	HuffmanTree Huff;  
	for(int i=0;i<A_length;i++){
		Huff = Create();
		Huff->weight = A[i];
		H->data[++H->size] = Huff;
	}
	// 调整堆 
	adjust(H);
}


// 删除最小堆元素
HuffmanTree Delete(MinHeap H){
	int parent,child;
	HuffmanTree T = H->data[1];  // 取出根结点的哈夫曼树 
	HuffmanTree tmp = H->data[H->size--]; // 取出最后一个结点哈夫曼树的权值 
	for(parent=1;parent*2<=H->size;parent = child){
		child = 2 * parent;
		if((child!=H->size) && (H->data[child+1]->weight < H->data[child]->weight))
			child++;
		if(H->data[child]->weight >= tmp->weight)
			break;
		else
			H->data[parent] = H->data[child];
	} 
	H->data[parent] = tmp;
	// 构造一个 HuffmanTree 结点,附上刚才取出来的权值,返回该结点 
	return T;
}

// 插入一个哈夫曼树
void Insert(MinHeap H,HuffmanTree Huff){
	int weight = Huff->weight; // 取出权值
	int i = ++H->size;
	for(;H->data[i/2]->weight > weight;i/=2)
		H->data[i] = H->data[i/2];
	H->data[i] = Huff;
} 

//遍历 
void PreOrderTraversal(HuffmanTree Huff){
	if(Huff){
		cout<<Huff->weight<<" ";
		PreOrderTraversal(Huff->Left);
		PreOrderTraversal(Huff->right);
	}
}

// 哈夫曼树的构造 
HuffmanTree Huffman(MinHeap H){
	HuffmanTree T;
	BuildMinHeap(H); // 建堆 
	int times = H->size;
	// 做 times-1 次合并 
	for(int i=1;i<times;i++){
		T = (HuffmanTree)malloc(sizeof(struct TreeNode));
		T->Left = Delete(H);   // 从堆中删除一个结点,作为新 T 的左子结点 
		T->right = Delete(H);  // 从堆中删除一个结点,作为新 T 的右子结点 
		T->weight = T->Left->weight + T->right->weight; // 重新计算权值 
		Insert(H,T);  // 再加进堆中 
	}
	T = Delete(H);
	return T;
} 



int main(){
	MinHeap H;
	HuffmanTree Huff; 
	H = create();
	Huff = Huffman(H); 
	PreOrderTraversal(Huff);
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值