👴周末两天也算就看了哈夫曼树和编码问题了,进度巨慢,还有一个骑士周游算法跑到现在还没出结果。。。。。
趁热打铁,把自己的一些心得分享给大家。
重点在后面。。。
话不多说 都在代码注释里了
这个是只操作整数数组
#include <iostream>
#define N 10
using namespace std;
void Find_Min_SubMin_Nums(int data[],int n)
{
int min1,min2;//min1 是最小数 min2 是次小数d
min1 = min2 = 30000;//先让这两个数取得尽量大
for(int i = 0;i<n;i++)
{
if(data[i]<min1)//数组中某一数据比最小值还小? 那么最小值应该是它
//之前的最小值暂时算是第二小的数 所以可以把之前的最小值给次小值
{
min2 = min1;
min1 = data[i];
}
//找到了最小数 就没必要关心次小数了 因为当前的min1 min2 都取到了最小
else if(data[i]<min2)//此时的情况是说,扫描到的数字 比最小值大 但是比次小值小
//这好办 直接把次小值替换
min2 = data[i];
}
//如此循环下去 得到的就是最小值和次小值了
cout<<"最小值:"<<min1<<endl;
cout<<"次小值:"<<min2<<endl;
}
int main()
{
int a[N] = {-1,-9,10,-127,1999,1,-126,2,3,4};
Find_Min_SubMin_Nums(a,N);
return 0;
}
如果用在哈夫曼树的生成中应该是这样的
嘿嘿 开始之前我先复习一下哈夫曼树的生成过程
首先 先定义一个带有权重的树结点的结构体
typedef char ElemType;
struct HfTreeNode
{
ElemType data;
int parent; //双亲结点
int lchild,rchild;//双亲结点
int weight;//权重
};
接下来 简要描述一下哈夫曼树的生成过程
它的思想就是找一种最优的方法 使得WPL取得最小 有点类似于最优方法求解的味道 下面我尽量用简单直白的语言来描述这个过程
以下的过程基于以下假设:
1.已知某一顺序表的元素个数
2.元素的数据类型是char
3.生成的哈夫曼序列最大值不超过256
假设HfTreeNode possible[256] 结构体数组的元素个数为N(N也是叶子结点数)
那么有这个数组生成的哈夫曼树的总结点数为 2N - 1
我们使用possible的0~N-1位置来存放叶子结点
N~2N-2位置存放非叶子结点
类似于下图
了解了这个 就成功了一半了
由于树结点的结构中有双亲结点、兄弟结点的值 我们可以在函数刚开始时对整个结构体数组进行初始化 将双亲结点、兄弟节点的值都指向-1.在后面操作时在对其进行赋值 有人可能会问了 这么做的目的是什么?emmmm 下面我用一个例子来说明一下
这是第一步走完了 结果 非叶子结点4时叶子结点0和1的双亲结点 那么结点0 和 结点 1的双亲结点值就被我给定了 那么 下一步 在序号0——4中寻找最小权和次小权元素时 结点0和结点1就不会参加了 所以第二步应该为:
这步完成后 同样 序号2和4作为序号5的子节点 下一次寻找权值时它们也不会参与
依次类推。。。。
好了,下面回到正题 其实上面的讲解基本已经把这个算法给讲完了
下面总结一下:
step1: 将 possible中每个成员的双亲结点、兄弟结点全部给-1.
step2: 由于叶子结点已经在队列中,从0——N-1.下面我们使用for循环结构对剩余的N-1个非叶子结点进行操作,即从N——2*N-2.
for(int i = n;i<2*n-1;i++)
【咦?N的大小写貌似搞错了,不影响大局 : ) 】
假设 i = N;此时应该从它前面的所有元素中找到 最小 和 次小 。
具体操作可以参看前面的找最小和次小的方法。
找到之后 ,并进行一系列的数据更新操作,哈夫曼树基本就建立起来了。不过这期间可能会遇到一些其他的问题,比如代码段第69行的那个“=”是否需要。李春葆的《数据结构》中是没有的,正确与否。嘿嘿嘿,这就需要你自己来判断了。我就再次不表了。(打字太累了)
其实我们可以通过一个友好的界面来获取possible数组的值,这个实现起来应该不难。
真是托了陶老师的福,👴要自学这门课。
有精力了 我再上传 哈夫曼编码的一些问题。。。
希望有大佬带带小弟。
附录代码:
(友好界面+建立哈夫曼树+哈夫曼编码)
#include <iostream>
#include <malloc.h>
#define MAXSIZE 256
#define N 4
#define M 2*N-1
typedef char ElemType;
struct HfTreeNode
{
ElemType data;
int parent; //双亲结点
int lchild,rchild;//双亲结点
int weight;//权重
};
struct HfCode
{
ElemType data;
ElemType Code[N];
int length;
};
using namespace std;
//根据用户输入得到数据
int Get_Weight(HfTreeNode possible[],char *str)
{
int i = 0,k = 0;
int temp[MAXSIZE] = {0};
while (str[i] != 0)
{
temp[(int)str[i]]++;
i++;
}
for(int i = 0;i<MAXSIZE;i++)
{
if(temp[i] != 0)
{
possible[k].weight = temp[i];
possible[k].data = (char)i;
k++;
}
}
return k;
}
//创建一个哈夫曼树
void Creat_HfTree(HfTreeNode possible[],int n)
{
int min1,min2;//min1 最小数 min2 次小数
int lnode,rnode;
//1. 先将2*n-1的空间都给初始化
for(int i = 0;i<2*n - 1;i++)
{
possible[i].parent = -1;
possible[i].lchild = possible[i].rchild = -1;
}
//2. 叶子结点已经确定下来了 下面就确定非叶子结点就可以了
//叶子结点有n个,数组下标到n-1;所以非叶子结点应从n开始
//找到了最小结点后 我们应当这个非叶子结点的左孩子---->最小结点 右孩子---->次小结点
//更新双亲节点的下标
for(int i = n;i<2*n-1;i++)
{
//2.1 从0——i中搜寻到两个权值最小的结点是我们的目标
min1 = min2 = 100000;//这两个数的初始值应尽量给小
lnode = rnode = -1;
for(int j = 0;j<i;j++)
{
if(possible[j].parent == -1)//不等于-1说明该结点已经被处理过了 我们只处理未处理过的结点
{
if(possible[j].weight <= min1)//数组中某一数据比最小值还小? 那么最小值应该是它
//之前的最小值暂时算是第二小的数 所以可以把之前的最小值给次小值
{
min2 = min1;rnode = lnode;
min1 = possible[j].weight;
lnode = j;
}//找到了最小数 就没必要关心次小数了 因为当前的min1 min2 都取到了最小
else if(possible[j].weight < min2)//此时的情况是说,扫描到的数字 比最小值大 但是比次小值小
//这好办 直接把次小值替换
{
min2 = possible[j].weight;
rnode = j;
}
//如此循环下去 得到的就是最小值和次小值了
}
}
possible[i].weight = possible[lnode].weight + possible[rnode].weight;
possible[i].lchild = lnode;
possible[i].rchild = rnode;
possible[lnode].parent = i; possible[rnode].parent = i;
}
}
//处理编码顺序
void Deal_Order_Code(HfCode code[],int n)
{
int Flag = 0;//用于标记第一次非0时的情况
for(int i = 0;i<n;i++)
{
for(int j = 0;j<=code[i].length;j++)
{
if(code[i].Code[j] != 0)
{
Flag = j;break;
}
}
for(int k = Flag,l = 0;k<=code[i].length;k++,l++)
{
code[i].Code[l] = code[i].Code[k];
code[i].Code[k] = 0;
}
}
}
//根据哈夫曼树得到哈夫曼编码
void Get_HfCode(HfTreeNode possible[],int n,HfCode code[])
{
int f,temp,Length;
for(int i = 0;i<n;i++)//获取n个叶子结点的哈夫曼编码
{
temp = i;//temp 表示当前结点
f = possible[i].parent;
code[i].length = n;
code[i].data = possible[i].data;
Length = code[i].length;
while (f != -1)//只要双亲结点不等于-1 就说明还没有到根节点
{
if(possible[f].lchild == temp)//双亲结点的左孩子是当前结点 说明双亲结点是从该结点过来的
{
code[i].Code[Length] = '0';
Length--;
}
else
{
code[i].Code[Length] = '1';
Length--;
}
//执行完该结点的操作后 由于还没有到达根节点 要往上继续走
//更新
temp = f;//temp 现在是父亲了
f = possible[f].parent;//f 是父亲的父亲 也就是爷爷了
}
}
Deal_Order_Code(code,n);
}
//显示哈夫曼编码
void Display(HfCode code[],int n)
{
for(int i = 0;i<n;i++)
{
cout<<code[i].data<<"的哈夫曼编码是:"<<code[i].Code<<endl;
}
}
int main()
{
int k;
char choice;
char str[MAXSIZE];
cout<<"请输入字符串"<<endl;
//cin.get(str,MAXSIZE);
//cin.get();
while (cin.get(str,MAXSIZE))
{
HfTreeNode nums[MAXSIZE] = {0};
HfCode MyCode[MAXSIZE] = {};
k = Get_Weight(nums,str);
Creat_HfTree(nums,k);
Get_HfCode(nums,k,MyCode);
cout<<"下面是输入字符串的哈夫曼编码:"<<endl;
Display(MyCode,k);
cout<<"请问还要输入吗?(Y/N)"<<endl;
cin>>choice;
cin.get();
if(choice == 'N')
break;
cout<<"请继续输入"<<endl;
//cin.get(str,MAXSIZE);
//cin.get();
}
cout<<"再见!"<<endl;
cout<<endl;
return 0;
}
来源:CSDN
作者:CCurtain
链接:https://blog.csdn.net/CCurtain/article/details/103656575