数据结构11-哈夫曼树和N皇后问题

①哈夫曼树

一、定义

在认识哈夫曼树之前,我们需要知道几个定义:

1、路径:从树中一个结点到另一个结点的分支构成这两个结点的路径,如下图根节点A到叶子结点G的路径为A->C->G。

2、路径长度:简而言之就是路径上的分支数目,如A到G经过了两条边,则该路径长度为2

3、权:赋予某个实体的一个量,是对实体的某个或某些属性的数值化描述。在数据结构中,实体有结点(元素)和边(关系)两大类,所以对应有结点权和边权。

4、结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积。

5、树的带权路径长度:树中所有叶子结点的带权路径长度之和,通常记作WPL。

哈夫曼树:给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,则称该二叉树为哈夫曼树,也被称为最优二叉树

单个结点的类型定义:

typedef double DataType; 

typedef struct HTNode{
	DataType weight;     //权值
	int parent;          //父节点
	int lc, rc;          //左右孩子
}*HuffmanTree;

二、核心函数

 1、找出权值最小的两个

void Select(HuffmanTree HT,int n,int &s1,int &s2) {
	int minum;
	int i;
    // 找第一个最小值
	for(i=1;i<=n;i++){
		if(HT[i].parent == 0){
			minum = i;
			break;
		}
	}
	for(i = 1;i<=n;i++){
		if(HT[i].parent == 0){
			if(HT[i].weight<HT[minum].weight){
				minum = i;
			}
		}
	}
	s1 = minum;  // 把第一个最小值给s1
	for(i=1;i<=n;i++){  // 找第二个最小值
		if(HT[i].parent == 0 && i!=s1){
			minum = i;
			break;
		}
	}
	for(i = 1;i<=n;i++){
		if(HT[i].parent == 0 && i!=s1){
			if(HT[minum].weight<HT[minum].weight){
				minum = i;
			}
		}
	}
	s2 = minum;
}

2、建立哈夫曼树

// 建立哈夫曼树 
void CreatHuffmanTree(HuffmanTree HT,int n){
	int m,i,s1,s2;
	if(n<=1){
		return ;
	}
	m = 2*n-1;			    // 数组共2n-1个元素
	HT = new HTNode[m+1];	// 0号单元未用,HT[m]表示根节点 
	for(i=1;i<=m;i++){		
		HT[i].lch = 0;
		HT[i].rch = 0;
		HT[i].parent = 0;
	} 
	for(i = 1;i<=n;i++){	// 输入前n个元素的权值 
		scanf("%d",HT[i].weight);
	}
	// 初始化结束,下面开始建立哈夫曼树 
	for(i=n+1;i<=m;i++){
		Select(HT,i-1,s1,s2);
		HT[s1].parent = i;
		HT[s2].parent = i;
		HT[i].lch = s1;
		HT[i].rch = s2;
		HT[i].weight = HT[s1].weight+HT[s2].weight;
	} 
} 

3、哈夫曼编码

// 哈夫曼编码 
void  CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
	HC = new char*[n+1];
	char *cd = new char[n];
	cd[n-1] = '\0';
	int start,c,f,i;
	for(i=1;i<=n;i++){
		start = n-1;
		c = i;
		f = HT[i].parent;
		while(f!=0){
			start--;
			if(HT[f].lch == c){
				cd[start] = '0';
			}else{
				cd[start] = '1';
			}
			c = f;
			f = HT[f].parent;
		}
		HC[i] = new char[n-start];
		strcpy(HC[i],&cd[start]);
	}
	delete cd;		

三、完整代码

# include<stdio.h>
# include<string.h> 
 
//存放哈夫曼编码
typedef char** HuffmanCode;
 
// 结点类型定义
typedef struct{
	DataType weight;
	int parent,lch,rch;
}HTNode,*HuffmanTree; 
 
// 找出森林中权值最小的两个
void Select(HuffmanTree HT,int n,int &s1,int &s2) {
	int minum;
	int i;
	for(i=1;i<=n;i++){
		if(HT[i].parent == 0){
			minum = i;
			break;
		}
	}
	for(i = 1;i<=n;i++){
		if(HT[i].parent == 0){
			if(HT[i].weight<HT[minum].weight){
				minum = i;
			}
		}
	}
	s1 = minum;
	for(i=1;i<=n;i++){
		if(HT[i].parent == 0&& i!=s1){
			minum = i;
			break;
		}
	}
	for(i = 1;i<=n;i++){
		if(HT[i].parent == 0&& i!=s1){
			if(HT[minum].weight<HT[minum].weight){
				minum = i;
			}
		}
	}
	s2 = minum;
}
 
// 建立哈夫曼树 
void CreatHuffmanTree(HuffmanTree HT,int n){
	int m,i,s1,s2;
	if(n<=1){
		return ;
	}
	m = 2*n-1;			// 数组共2n-1个元素
	HT = new HTNode[m+1];	// 0号单元未用,HT[m]表示根节点 
	for(i=1;i<=m;i++){		// 将2n-1个元素的lch,rch,parent设置为0 
		HT[i].lch = 0;
		HT[i].rch = 0;
		HT[i].parent = 0;
	} 
	for(i = 1;i<=n;i++){	// 输入前n个元素的weight值 
		scanf("%d",HT[i].weight);
	}
	// 初始化结束,下面开始建立哈夫曼树 
	for(i=n+1;i<=m;i++){
		Select(HT,i-1,s1,s2);
		HT[s1].parent = i;
		HT[s2].parent = i;
		HT[i].lch = s1;
		HT[i].rch = s2;
		HT[i].weight = HT[s1].weight+HT[s2].weight;
	} 
} 

// 哈夫曼编码 
void  CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
	HC = new char*[n+1];
	char *cd = new char[n];
	cd[n-1] = '\0';
	int start,c,f,i;
	for(i=1;i<=n;i++){
		start = n-1;
		c = i;
		f = HT[i].parent;
		while(f!=0){
			start--;
			if(HT[f].lch == c){
				cd[start] = '0';
			}else{
				cd[start] = '1';
			}
			c = f;
			f = HT[f].parent;
		}
		HC[i] = new char[n-start];
		strcpy(HC[i],&cd[start]);
	}
	delete cd;		// 释放临时空间 
}

//主函数
int main()
{
	int n = 0;
	printf("请输入数据个数:");
	scanf("%d", &n);
	DataType* w = (DataType*)malloc(sizeof(DataType)*n);
	if (w == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	printf("请输入数据:");
	for (int i = 0; i < n; i++)
	{
		scanf("%lf", &w[i]);
	}
	CreatHuffmanTree HT;
	CreateHuff(HT, w, n); // 构建哈夫曼树

	CreatHuffmanCode HC;
	HuffCoding(HT, HC, n); // 构建哈夫曼编码

	for (int i = 1; i <= n; i++) // 打印哈夫曼编码
	{
		printf("数据%.2lf的编码为:%s\n", HT[i].weight, HC[i]);
	}
	free(w);
	return 0;
}

②N皇后问题

N皇后问题是一个经典的问题,在一个N*N的棋盘上放置N个皇后,每行一个并使其不能互相攻击(同一行、同一列、同一斜线上的皇后都会自动攻击)

下面主要展示其中的一种解法:递归回溯法

一、放置皇后

bool place(int* paraSolution,int paraT){
	int j;
	for(j = 1;j < paraT;j++){
		if((abs(paraT - j) == abs(paraSolution[j] - paraSolution[paraT])) || (paraSolution[j] == paraSolution[paraT]))
        return false;		
 } 
 return true;
}

二、回溯

void backtracking(int* paraSolution,int paraN,int paraT){
	int i;
	if(paraT > paraN){
		for(i = 1;i <= paraN;i++)
			printf("%d",paraSolution[i]);
		printf("\r\n");
	}else{
		for(i = 1;i <= paraN;i++){
			paraSolution[paraT] = i;
			if(place(paraSolution,paraT))
			   backtracking(paraSolution,paraN,paraT + 1);
		}
	}
}

三、完整代码

#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <stdbool.h>

bool place(int* paraSolution,int paraT){
	int j;
	for(j = 1;j < paraT;j++){
		if((abs(paraT - j) == abs(paraSolution[j] - paraSolution[paraT])) || (paraSolution[j] == paraSolution[paraT]))
        return false;		
 } 
 return true;
}

void backtracking(int* paraSolution,int paraN,int paraT){
	int i;
	if(paraT > paraN){
		for(i = 1;i <= paraN;i++)
			printf("%d",paraSolution[i]);
		printf("\r\n");
	}else{
		for(i = 1;i <= paraN;i++){
			paraSolution[paraT] = i;
			if(place(paraSolution,paraT))
			   backtracking(paraSolution,paraN,paraT + 1);
		}
	}
}

void nQueen(int paraN){
	int i;
	int* solution = (int*)malloc((paraN + 1)*sizeof(int));
	for(i = 0;i <= paraN;i++)
	solution[i] = 0;
	
	backtracking(solution,paraN,1);
}

int main(){
	nQueen(10);
	return 1; 
}

四、测试结果

当n=4时,测试结果为

当n=5时,测试结果为

 

 五、总结

对于n皇后问题有多种算法,上面采用的只是递归的回溯算法,处理速度比较慢,其他算法以后另做研究

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值