算法设计第七周(应用哈夫曼算法解决文件归并问题)

一、【实验目的】

(1)进一步理解贪心法的设计思想

(2)掌握哈夫曼算法的具体应用

(3)比较不同的文件归并策略,探讨最优算法。

二、【实验内容】

设S={f1,...,fn}是一组不同的长度的有序文件构成的集合,其中fi表示第i个文件的记录个数,现使用二分归并法将这些文件归并成一个有序文件,归并过程可以看成是一棵二叉树.参考教材P102例4.7,请采用两种方法进行二分归并,其中一种为哈夫曼算法。

提示:其中n个文件可用n个数组模拟,文件的内容为数组中已排好序的整数;可以用教材例4.7中定义的S集合中6个文件的长度作为数组长度进行测试。注意,要编写两个数组进行顺序归并的程序。

三、实验源代码

四、实验结果

五、实验分析与总结

实验分析:

++要求完成以下两种归并++

按照哈夫曼算法的思路,先取最小的两个点进行归并,得到一个父结点。再放回去再选择两个小的。

⚠️错误写法

  • 这里用的优先队列实现了插入一个元素后排序且可以有队列的特性。
  • j用于叶子结点的创建,i用于父结点的创建。
  • returnElement函数取最小的两个元素,再加起来放回去
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int n = 6;
vector<int> s = {21, 10, 32, 41, 18, 70};
vector<int> a = s;
vector<int> temp(10);
priority_queue<int, vector<int>, greater<int>> pq; //升序队列模板

struct treeNode{
	int weight;
	int parent;
	int leftchild, rightchild;
}tree[20];

void returnElement(int& element1, int& element2)
{
	if(!pq.empty())
	{
		element1 = pq.top();
		pq.pop();
		element2 = pq.top();
		pq.pop();
		int sum = element1 + element2;
		pq.push(sum);
	}
}

void CreateHuffmanTree(){ //二分归并法最优
	//输入
	for(auto e : s)
		pq.push(e);
		
	int j=1, element1, element2;
	//初始化-1
	for(int i=1; i<=2*n-1; i++)
	{
		tree[i].parent = -1,  tree[i].leftchild = -1; 
		tree[i].rightchild = -1;
	}
	for(int i=n; i<=2*n-1; i++)
	{
		//左右孩子结点
		returnElement(element1, element2);
		tree[j].weight = element1;
		j++;
		tree[j].weight = element2;
		j++;
		//父节点
		tree[i].weight = element1 + element2;
		tree[i].leftchild = j-2;
		tree[i].rightchild = j-1;
	}
	
}
//由于中文乱码,所以输出英文
void printTree()
{
    for (int i = 1; i <=2*n-1; i++)
    {
    	cout << setw(7) << "i:" << i ;
        cout << setw(7) << "node:" << tree[i].weight << "\t";
        cout << setw(7) << "parent:" << tree[tree[i].parent].weight << "\t";
        cout << setw(7) << "leftchild:" << tree[tree[i].leftchild].weight << "\t";
        cout << setw(7) << "rightchild:" << tree[tree[i].rightchild].weight << endl;
    }
}

int main()
{
	CreateHuffmanTree();
	printTree();
	return 0;
}

运行结果:

  • 叶子结点和父结点交叉存放
  • -1的值未打印出来
  • 有一些数据被覆盖掉了


对比书上的图,缺项多项

正确写法:

【【数据结构】构造哈夫曼树手写代码】https://www.bilibili.com/video/BV1EV4y1V7Vx/?share_source=copy_web&vd_source=7ffbd7feaeedb3d59fb21e59435a53d8 改进:

returnElement函数弃用,改成每次在tree中寻找最小元素、和次小元素的下标。

先把叶子结点存入,以免出现交叉存放和覆盖数据

先把叶子结点存入后,直接更新父结点的值就可以了,叶子结点的parent指向父结点的下标

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int n = 6;
vector<int> s = {21, 10, 32, 41, 18, 70};
vector<int> a = s;
vector<int> temp(10);
//priority_queue<int, vector<int>, greater<int>> pq; //升序队列模板

struct treeNode{
	int weight;
	int parent;
	int leftchild, rightchild;
}tree[20];

void selectMinElementIndex(int n, int& index1, int& index2)
{
	int min = 9e8; //打擂台,用于确定最小的那个
	//寻找最小的权值下标
	for(int i=1; i<= n; i++)
	{
		if(tree[i].parent == -1 && tree[i].weight < min)
		{
			min = tree[i].weight;
			index1 = i;
		}
	}
	min = 9e8;
	//寻找次小的权值下标
	for(int i=1; i<= n; i++)
	{
		if(tree[i].parent == -1 && tree[i].weight < min && i != index1)
		{
			min = tree[i].weight;
			index2 = i;
		}
	}
}

void CreateHuffmanTree(){ //二分归并法最优
	sort(s.begin(), s.end());	
	int index1, index2;
	//初始化-1
	for(int i=1; i<=2*n-1; i++)
	{
		tree[i].parent = -1,  tree[i].leftchild = -1; 
		tree[i].rightchild = -1;
	}
	//放入叶子结点
	for(int i=1; i<=n; i++)
	{
		tree[i].weight = s[i-1];
	}
	
	for(int i=n+1; i<=2*n-1; i++)
	{
		//传入了i-1,保证在新建立的父节点和叶子结点中寻找两个最小
		selectMinElementIndex(i-1, index1, index2);
		// 左右孩子结点指向父结点
		tree[index1].parent = i;
		tree[index2].parent = i;
		//父结点更新值
		tree[i].weight = tree[index1].weight + tree[index2].weight;
		tree[i].leftchild = index1;
		tree[i].rightchild = index2;
	}
}
//由于中文乱码,所以输出英文
void printTree()
{
    for (int i = 1; i <= 2*n-1; i++)
    {
    	cout << setw(7) << "i:" << i << "  ";
        cout << setw(7) << "node:" << tree[i].weight << "\t";
        cout << setw(7) << "parent:" <<  (tree[i].parent != -1 ? tree[tree[i].parent].weight : -1) << "\t";
        cout << setw(7) << "leftchild:" <<  (tree[i].leftchild != -1 ? tree[tree[i].leftchild].weight : -1) << "\t";
        cout << setw(7) << "rightchild:" << (tree[i].rightchild != -1 ? tree[tree[i].rightchild].weight : -1) << endl;
    }
}

void CreateATree(){ //另一种二分归并法
	
	int j=1;
	//初始化-1
	for(int i=1; i<=2*n-1; i++)
	{
		tree[i].parent = -1,  tree[i].leftchild = -1; 
		tree[i].rightchild = -1;
	}
	//放入叶子结点
	for(int i=1; i<=n; i++)
	{
		tree[i].weight = s[i-1];
	}
	
	for(int i=n+1; i<=2*n-1; i++)
	{
		// 左右孩子结点指向父结点,j直接选两个平推过去
		tree[j].parent = i;
		tree[j+1].parent = i;
		//父结点更新值
		tree[i].weight = tree[j].weight + tree[j+1].weight;
		tree[i].leftchild = j;
		tree[i].rightchild = j+1;
		j += 2;
	}
}

int main()
{
	cout << "The another method:" << endl;
	CreateATree();
	printTree();
	cout << "The best method:" << endl;
	CreateHuffmanTree();
	printTree();
	return 0;
}

【运行结果】

相关推荐
yuanbenshidiaos4 分钟前
C++----------函数的调用机制
java·c++·算法
唐叔在学习8 分钟前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA28 分钟前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo30 分钟前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc37 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
游是水里的游2 小时前
【算法day20】回溯:子集与全排列问题
算法
yoyobravery2 小时前
c语言大一期末复习
c语言·开发语言·算法
Jiude2 小时前
算法题题解记录——双变量问题的 “枚举右,维护左”
python·算法·面试
被AI抢饭碗的人2 小时前
算法题(13):异或变换
算法
nuyoah♂3 小时前
DAY36|动态规划Part04|LeetCode:1049. 最后一块石头的重量 II、494. 目标和、474.一和零
算法·leetcode·动态规划