1.8.课设实验-数据结构-哈夫曼树的建立与应用

题目:

使用C语言/C++实现哈夫曼树的建立与应用。

参考代码:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//哈夫曼树结构体 
typedef struct HFTNode
{
	//1.子树数据域 
	int data;
	//2.左、右孩子指针 
	struct HFTNode *lchild,*rchild;
	//3.记录当前子树是否是其他子树合成的(是:true;不是:false)
	bool flag;
	//4.定义变量记录当前结点(子树)的哈夫曼编码
	char code[10]; 
}HFTNode,*HFTree;

//存储森林的顺序表
typedef struct
{
	//1.树的数组
	HFTree list[20];
	//2.记录当前树的个数
	int lengeh;
	//3.顺序表的头、尾指针
	int front,rear; 
}HFTlist;

/*-------------------------------赵旭文-------------------------------*/
//森林顺序表添加元素的函数
/*形参是顺序表、要添加的数据域的值、数据所添加的位置*/
void InsertTree(HFTlist &L,int data,int index)
{
	//1.给森林顺序表的其中一个元素申请空间 
	HFTree a = (HFTree)malloc( sizeof(HFTNode) );
	//2.分配数据 
	a->data = data;
	//3.设置左、右子树,初始都为NULL 
	a->lchild = NULL;
	a->rchild = NULL;
	//4.设置是否为合成后的树,初始都不是 
	a->flag = false;
	//5.设置位置 
	L.list[index] = a; 
	//6.森林顺序表的长度加一
	L.lengeh++; 
} 

//初始化森林顺序表的函数
HFTlist InitList(HFTlist &L)
{
	//1.添加元素
	InsertTree(L,10,0);
	InsertTree(L,12,1);
	InsertTree(L,16,2);
	InsertTree(L,21,3);
	InsertTree(L,30,4);
	InsertTree(L,22,5);
	//2.设置头尾指针
	L.front = 0;
	L.rear = L.lengeh - 1;
	//3.返回
	return L; 
} 
/*-------------------------------赵旭文-------------------------------*/


/*-------------------------------黄黄-------------------------------*/
//求出划分索引的函数
int Partition(HFTlist &L,int low,int high)
{
	//1.将当前表中第一个元素设为枢轴,对表进行划分 
    HFTree pivotNode = L.list[low];
    int pivot = pivotNode->data;
	/*没有直接使用int pivot = L.list[low]->data; 是防止之后产生指针被覆盖*/
	//2.开始循环
	while( low < high )
	{
		//2.1.找出比pivot小的元素 
		while( low < high && L.list[high]->data >= pivot ) high--;
		//2.2.移到左边
		L.list[low] = L.list[high];
		//2.3.找出比pivot大的元素
		while( low < high && L.list[low]->data <= pivot ) low++;
		//2.4.移到右边 
		L.list[high] = L.list[low];
	 } 
	 //3.枢轴元素放到最终位置即low==high的位置上
	 L.list[low] = pivotNode;
	 //4.返回存放枢轴元素的最终位置
	 return low; 
} 

//用于对森林顺序表里的元素进行升序排序的函数(采用冒泡排序)
/*采用快速排序是因为数据元素个数可控,采用递归安全且高效*/
/*这里头、尾指针不能直接用L.front和L.rear,用的话对后续森林顺序表的使用有影响,
因此传入形参int low,int high,记录头尾指针的值*/
void QuickSort(HFTlist &L,int low,int high)
{
	//只要头指针小于尾指针,就一直递归调用函数进行排序,头指针等于尾指针时就说明确定了一个元素的位置 
	if( low < high )
	{
		//1.得出划分索引
		int pivotpos = Partition( L , low , high );
		//2.对左子表进行排序
		QuickSort( L , low , pivotpos-1 );
		//3.对右子表进行排序
		QuickSort( L , pivotpos+1 , high ); 
	} 
} 
/*-------------------------------黄黄-------------------------------*/


/*-------------------------------青格勒-------------------------------*/
//创建哈夫曼树的函数
/*形参需要root即哈夫曼树的子树、森林顺序表即插入的子树*/
HFTree CreateHFTree(HFTree &root,HFTlist &L)
{
	//1.首先对初始森林顺序表进行排序
	/*初始时只需要这一次整体排序使得森林顺序表有序即可,
	因为后续的操作都是在保证有序的情况下进行,就无需再整体排序了 */
    QuickSort(L, L.front, L.rear);
    //2.当森林中还有多于1棵树时,继续合并
    while (L.rear - L.front + 1 > 1)
    {
        //3.取森林顺序表前两个最小的元素
        /*因为每一次合并最多并两个元素*/
        HFTree t1 = L.list[L.front];
        HFTree t2 = L.list[L.front + 1];
        //4.创建新的父结点(本质是一个更大的树)
        /*4.1.给父结点申请空间*/
        HFTree parent = (HFTree)malloc(sizeof(HFTNode));
        /*4.2.给该父结点赋数据值*/
        parent->data = t1->data + t2->data;
        /*4.3.设置左、右孩子,并且记录其中一个编码(左为0,右为1)*/
        parent->lchild = t1;
        parent->rchild = t2;
        /*4.4.标记为合成树*/
        parent->flag = true;  
        //5.从森林顺序表中移除已合并的两棵树
        L.front = L.front + 2;
        //6.将新合成的树加入到森林顺序表中
        /*6.1.开始找到合适的位置插入新树(保持有序即升序),初始位置设置为头部*/
        int insertPos = L.front;
        /*6.2.遍历森林顺序表查找合适的插入位置*/
        for (int i = L.front; i <= L.rear; i++)
        {
        	/*6.3.此时新生成的父结点数据域小于等于i索引上的数据域,
			因此要插在i的位置上,循环可以结束了,
			而且插在i上就保证了新生成的父结点数据域永远比之前i索引上的数据域先参与构成哈夫曼树*/
            if (parent->data <= L.list[i]->data)
            {
                insertPos = i;
                break;
            }
            /*6.4.此时说明父结点数据域比森林顺序表里的所有数据域都大,因此插在尾指针的后一个位置上*/
            if (i == L.rear)
            {
                insertPos = L.rear + 1;
            }
        }
        //7.移动元素为新树腾出位置
        for (int i = L.rear; i >= insertPos; i--)
        {
            L.list[i + 1] = L.list[i];
        }
        //8.插入新树
        L.list[insertPos] = parent;
        //9.修改尾指针 
        L.rear++;
        //10.更新根结点为最新的父结点 
        root = parent;
    }
    return root;
} 
/*-------------------------------青格勒-------------------------------*/


/*-------------------------------吴佳峻-------------------------------*/
//遍历得到的哈夫曼树(先序遍历)
void PreHFTree(HFTree &root)
{
	//判断
	if( root != NULL )
	{
		printf("%d->",root->data);
		PreHFTree(root->lchild);
		PreHFTree(root->rchild);
	} 
} 

//用来求出叶子结点即参与构成哈夫曼树的结点的编码的函数
/*第二个形参是字符指针,指向地址,是一个字符串型,
而且本例中传的是字符串常量,所以要用const修饰,如果不用const修饰的话传常量会出现类型转换奔溃,
const修饰后直接传字符串常量即可, 
如果不用const修饰的话也可以,只需要传递第二个参数时强制转换为char*型即可*/
void getHFTNodeCode(HFTree root,const char* parentCode)
{
	//1.判断当前哈夫曼树是否为空,为空不存在哈夫编码 
    if (root == NULL) return;
    //2.此时哈夫曼树不为空
	//3.定义变量记录哈夫曼编码 
    char currentCode[20];
    //4.给currentCode赋初值,不赋初值会出现随机数据,对于之后的操作会有影响 
    strcpy(currentCode, parentCode);
    //5.对左孩子进行递归判断 
    if (root->lchild != NULL) {
    	/*5.1.定义变量记录左编码*/
        char leftCode[20];
        /*5.2.此时左孩子不为空,就把0拼接上去*/
        strcpy(leftCode, currentCode);
        strcat(leftCode, "0");
        /*5.3.递归调用拼接下一个*/
        getHFTNodeCode(root->lchild, leftCode);
    }
    //6.对右孩子进行递归判断 
    if (root->rchild != NULL) {
    	/*6.1.定义变量记录右编码*/
        char rightCode[20];
        /*6.2.此时右孩子不为空,就把1拼接上去*/
        strcpy(rightCode, currentCode);
        strcat(rightCode, "1"); 
        /*6.3.递归调用拼接下一个*/
        getHFTNodeCode(root->rchild, rightCode);
    }
    //7.如果是叶子结点就打印编码,因为叶子结点存储完整编码
    if (root->lchild == NULL && root->rchild == NULL) {
        strcpy(root->code, currentCode);
        printf("数据%d的哈夫曼编码: %s\n", root->data, root->code);
    }
}
/*-------------------------------吴佳峻-------------------------------*/


int main()
{
	//1.创建一个森林顺序表,森林就是树的集合 
	HFTlist L;
	//2.森林顺序表未初始化时长度为0 
	L.lengeh = 0; /*InitList函数接收的是未初始化的L,其length不赋初值可能包含随机值*/
	//2.初始化森林顺序表
	L = InitList( L );
	/*定义一个森林顺序表,把L复制过去,为了之后*/
	/*遍历初始化得到的森林顺序表*/
	printf("当前参与构成哈夫曼树的子树有:");
	for( int i=L.front ; i<=L.rear ; i++ ) printf("%d,",L.list[i]->data);
	printf("\n");
	//3.创建一个树
	HFTree root = NULL; 
	//4.调用CreateHFTree函数创建哈夫曼树
	root = CreateHFTree( root , L ); 
	//5.遍历得到的哈夫曼树
	printf("由当前森林顺序表得到的哈夫曼树的先序遍历序列为:");
	PreHFTree(root); 
	printf("\n");
	//6.打印参与构成哈夫曼树的结点的编码
	getHFTNodeCode( root , "" ); 
	/*这里强制转换为char*型是因为getHFTNodeCode函数第二个形参是字符指针,
	不强制转换会出现不太安全的类型转换*/
	return 0;
}

相关推荐
无限进步_2 小时前
寻找数组中缺失数字:多种算法详解与比较
c语言·开发语言·数据结构·算法·排序算法·visual studio
xu_yule2 小时前
数据结构(4)链表概念+单链表实现
数据结构·算法·链表
缘三水3 小时前
【C语言】13.指针(3)
c语言·开发语言·指针·语法
!停3 小时前
深入理解指针3
c语言
此生只爱蛋3 小时前
【Redis】浅谈数据结构和内部编码和单线程架构
数据结构·数据库·redis
山峰哥3 小时前
现代 C++ 的炼金术:铸就高性能与高可维护性的工程实践
java·开发语言·前端·数据结构·c++
SoleMotive.3 小时前
Mysql底层的数据结构,为什么用B+树,如果在内存中,B树和B+树查询效率怎么样
数据结构·b树·mysql
赖small强3 小时前
【Linux C/C++开发】深入解析 Linux C/C++ 中的 Deferred Crash (延迟崩溃)
linux·c语言·c++·asan·core dump·延迟奔溃·mprotect
代码雕刻家3 小时前
1.10.课设实验-数据结构-查找-机票查询
c语言·数据结构·算法