①哈夫曼树
一、定义
在认识哈夫曼树之前,我们需要知道几个定义:
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皇后问题有多种算法,上面采用的只是递归的回溯算法,处理速度比较慢,其他算法以后另做研究