若将树中结点赋给一个带有某种含义的数值,则该数值称为该结点的权。从根结点到该结点之间的路径长度与该结点的权的乘积,称为该结点的带权路径长度。
如图,叶子结点I的带权路径长度为 3 × 3 = 9
给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,则称该二叉树为哈夫曼树,也被称为最优二叉树。
根据树的带权路径长度的计算规则,我们不难理解:树的带权路径长度与其叶子结点的分布有关。
即便是两棵结构相同的二叉树,也会因为其叶子结点的分布不同,而导致两棵二叉树的带权路径长度不同。
构造一棵哈夫曼树:
1、初始状态下共有n个结点,结点的权值分别是给定的n个数,将他们视作n棵只有根结点的树。
2、合并其中根结点权值最小的两棵树,生成这两棵树的父结点,权值为这两个根结点的权值之和,这样树的数量就减少了一个。
3、重复操作2,直到只剩下一棵树为止,这棵树就是哈夫曼树。
//哈夫曼编码
void HUffumanco(HuffmanTree HT,HUffmancode HC,int n)//哈夫曼表存储在编码表HC中
{
HC=(HUffmancode)malloc(sizeof(char*)*(n+1));//数组的0号单元不使用,从1号单元开始所以数组HC大小为n+1
char *code=(char*)malloc(sizeof(char)*n);//分配临时存放编码的动态数组
code[n-1]='\0';//编码结束符
int i,start,c,p;
for(i=1;i<=n;i++)//逐个求哈夫曼编码
{ //从叶子结点开始向上回溯直到根结点
start=n-1;//start指针指向\0
c=i;
p=HT[c].parent;
while(p!=0)//直到父结点为0为止
{
start--;//回溯一次strat向前指1个位置
if(HT[p].Lchild==c)//结点c是p的左孩子
{
code[start]='0';
}
else//是p的右孩子
{
code[start]='1';
}
c=p;
p=HT[c].parent;
}
HC[i]=(char*)malloc(sizeof(char)*(n-start));
strcpy(HC[i],code[start]);//将求得的编码从临时空间复制到HC的当前行中
}
free(code);//释放临时空间
}
n皇后问题:
===============
当遇到一个岔路口,会有以下两种情况:
存在没走过的路。此时可以任意选一条没走过的路深入,只要记住我们所走过的路径即可。倘若下次再来到这个路口,便不再沿着走过的路径继续深入,而是沿着没走过的路径深入下去;
所有路都已经走过。如果所有岔路口都已经遍历,则回退至上一个最近的岔路口。
当遇到死胡同,便回退到刚才距离最近的岔路口。
不断前进并重复该过程,直到找到终点或回退到起点位置。
================
这就是n皇后的原理:回溯
#include <stdio.h>
#include <malloc.h>
#include <math.h>
bool place(int *paraSolution, int paraT)
{
//判断第paraT行是否在前几行的对角线上或者皇后在同一列上
//如果在对角线上那么第paraT行减去前面的某一行的行数的绝对值等于对应皇后的列数相减的绝对值
for (int 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)
{ // paraN皇后个数,paraT第几行
if (paraT > paraN)
{
//如果第paraN行的皇后都找到了的话就开始打印
for (int i = 1; i <= paraN; i++)
printf("%d ", paraSolution[i]);
printf("\r\n");
//示意图
// for (int i = 1; i <= paraN; i++)
// {
// for (int j = 1; j <= paraN; j++)
// {
// if (paraSolution[i] == j)
// printf("Q ");
// else
// printf(". ");
// }
// printf("\n");
// }
// printf("\n");
}
else
{
for (int i = 1; i <= paraN; i++)
{
paraSolution[paraT] = i; //第paraT行的皇后放在第i列(paraT,paraSolution[paraT])为皇后的坐标
if (place(paraSolution, paraT))
backtracking(paraSolution, paraN, paraT + 1);
//因为当第paraT行满足放一个皇后后就直接进行下一行皇后的判断了,所以当出现所有皇后出现一个解的时候
//就会继续返回倒数第二行往满足第一组解的下一列寻找满足的皇后如果找到满足的就进行下一列从头开始的皇后寻找
//如果没有满足的就返回到倒数第三行接着第一组解的下一列寻找满足的皇后,然后依次循环此操作。直至第一次调用该函数的i等于棋盘行数。
//该回溯法实际就像有n个for循环一样一层一层的进行,如果最里面的循环结束就会返回上一循环然后在上一次的基础上往后走又回到最后依次循环
//递归的优点在于改变n值依然可以计算,如果是循环的话就不能进行计算。必须改变循环的次数
}
}
}
void nQueen(int paraN)
{
int *solution = (int *)malloc((paraN + 1) * sizeof(int)); //申请paraN个空间用来存储每一行皇后的位置(列数)(paraT,solution[paraT])为皇后对应的坐标
for (int i = 0; i <= paraN; i++) //将每一行皇后的位置都初始化为0
solution[i] = 0;
backtracking(solution, paraN, 1);
}
int main()
{
nQueen(8);
return 1;
}