此代码包含了构建哈夫曼树的代码,在后面几段
- //计算一个二叉树的节点个数,可以把所有的节点入队列,然后观察其rear的值,就知道其节点的个数, 这是一种广度的遍历方法
- //下面的是一中深度的遍历的方法 当节点为NULL时候,返回0, 当节点无子节点时,返回1 , 否则得话返回左右节点的总数
- int Nodes(BTNode *b)
- {
- int num1 = 0, num2 = 0;
- if(b == NULL) return 0;
- else if(b->lchild == NULL && b->rchild == NULL) return 1;
- else
- {
- num1 = Nodes(b->lchild); //此处记录的是其左子树的所有子节点的数目
- num2 = Nodes(b->rchild); // 此处记录的是右子树所有节点的数目
- return (num1 + num2 + 1); //此处 返回其左子树的数目+右子树的数目+ 本身的数目(也就是那个1)
- }
- }
- void LeafNodes(BTNode *b, int &leafnum) //此函数记录叶子节点的数目, 与上面的函数类似,不过因为此函数不用计数 枝节点的数目, 所以没有num1 num2 等变量
- { // 当其找到一个叶子节点的时候,就把叶子节点的计数器 ++, 因为leafnum是 &传参, 所以不用担心 回代的问题
- if(b == NULL) return ;
- else if(b->lchild == NULL && b->rchild == NULL) ++leafnum;
- else
- {
- LeafNodes(b->lchild,leafnum);
- LeafNodes(b->rchild,leafnum);
- }
- }
- int Nodes2(BTNode *b) //此函数同样是求 树的叶子节点的数目
- {
- int num1 = 0, num2 = 0;
- if(b == NULL) return 0;
- else if(b->lchild == NULL && b->rchild == NULL) { return 1;}
- else
- {
- num1 = Nodes2(b->lchild);
- num2 = Nodes2(b->rchild);
- return (num1 + num2 ); //此处不同的是 没有 + 1, 产生的递增效果就是 枝节点 不会被递增上, 也就是说只有叶子节点才可以 对其产生作用。。。 此处加1 产生的作用是非常大的(因为递增的原因)
- }
- }
- void DNodes(BTNode *b, int &dnum) //此函数计算 有两个子节点的 节点的个数
- {
- if(b != NULL) //当节点不为空的时候进行操作
- {
- if(b->lchild != NULL && b->rchild != NULL) //此处表示 遇到了有双子节点的 节点
- ++dnum;
- DNodes(b->lchild,dnum); //遍历左子树
- DNodes(b->rchild,dnum); //遍历右子树
- }
- }
- int DNodes2(BTNode *b) //此函数是求树中 有双子节点的节点的个数
- {
- int num1 = 0, num2 = 0, n = 0;
- if(b == NULL) return 0;
- else if(b->lchild == NULL || b->rchild == NULL) n = 0; //上面两个表明不符合条件 把 n置为 0
- else { n = 1; }
- num1 = DNodes2(b->lchild);
- num2 = DNodes2(b->rchild);
- return (num2 + num1 + n); //由题意知, n的值只能为 0 或 1
- }
- void DNodes3(BTNode *b, int &dnum) //此函数也是求 一个树中有多少个 双节点
- {
- int num1 = 0, num2 = 0;
- if(b != NULL) //当节点不为空的时候进行
- {
- if(b->rchild == NULL || b->lchild == NULL) //当节点只有一个子节点的时候,进行递归操作
- {
- DNodes3(b->lchild, dnum);
- DNodes3(b->rchild, dnum);
- }else
- {
- ++dnum; //当节点有两个子节点的时候, 把计数器++, 然后继续执行递归操作
- DNodes3(b->lchild, dnum);
- DNodes3(b->rchild, dnum);
- }
- }
- }
- void DelTree(BTNode *b) //此函数用来释放 树中节点的所占用的空间, 释放只能采用后序遍历,不能使用先序或者中序遍历,因为中有将子树的空间释放掉后才能释放根节点的空间
- {
- if(b != NULL)
- { DelTree(b->lchild);
- DelTree(b->rchild);
- free(b);
- }
- }
- void Swap(BTNode *&b) //交换左右子树 用到的还是 后序 递归
- {
- BTNode *temp;
- if(b != NULL) //当节点存在的时候 进行操作
- {
- Swap(b->lchild);
- Swap(b->rchild);
- temp = b->lchild; //交换左右子树
- b->lchild = b->rchild;
- b->rchild = temp;
- }
- }
- //对树的递归操作 莫过于先 遍历左子树, 在遍历右子树, 这是一个方面。 另一个方面是 先序 中序 还是后序
- void Swap1(BTNode *b, BTNode *&b1) //此函数是由一个树,重新构建得到其另外一个左右子树交换的树。
- {
- if(b == NULL)
- {
- b1 = NULL;
- }
- else
- {
- b1 = (BTNode *)malloc(sizeof(BTNode));
- b1->data = b->data;
- Swap1(b->lchild, b1->rchild);
- Swap1(b->rchild, b1->lchild);
- }
- }
- void ancestor(BTNode *b,char r, char s) //此函数是求得 值为 r s 节点的最近的祖先节点
- { //不是一般性本例子采用后序遍历树, 并且假设 r 节点在 s 节点的左边
- BTNode *st[MAXSIZE], *temp, *temp2; //本函数的思路是,后序遍历树 把遇到的节点进栈(这时候入栈中的元素都是r的祖先节点),当先(因为规定r节点在s节点的左边)遇到值为r节点的时候,就把栈的所有节点的值放入path数组,用以记录访问过的值
- int top = -1; //当遇到值为s的节点时候,就比较path数组与栈中的节点值,直到第一个不同即为其共同的祖先节点(当出栈的时候把 其不是公共的节点给出去了)
- bool flag = true;
- char path[MAXSIZE];
- if(b != NULL)
- {
- temp = b;
- do //此循环是后序遍历
- {
- while(temp != NULL) //把左子树递归进栈
- {
- ++top;
- st[top] = temp;
- temp = temp->lchild;
- }
- flag = true;
- temp2 = NULL;
- while(flag && top > -1)
- {
- temp = st[top];
- if(temp->rchild == temp2) //当节点的右节点访问过 或者 其右节点为空的时候,就访问它
- {
- if(temp->data == r) //判断节点的值是否为r 如果是就给path数组赋值
- {
- for(int loop1=0; loop1 <= top; ++loop1)
- path[loop1] = st[loop1]->data;
- --top; //执行出栈操作
- temp2 = temp; //表示已经访问过,,即对其做标记
- }else if(temp->data == s)
- {
- int loop2 = 0;
- while(st[loop2]->data == path[loop2])
- ++loop2;
- cout<<r<<" 与 "<<s<<" , 最近的共同祖先是 :"<<path[--loop2]<<endl;
- return ;
- }else
- {
- --top;
- temp2 = temp;
- }
- }else
- {
- flag = false; //此语句块表示当其右子节点没有被访问的时候,就跳出循环用flag做标志变量, 然后对其右子树执行递归操作
- temp = temp->rchild;
- }
- }
- }while(top > -1);
- }
- }
- char path1[MAXSIZE];
- int top = -1;
- void AncestorPath(BTNode *b, char s) //此函数实现的是输出根节点到 值为 s 节点之间的路径,采用的递归操作
- { //这是一种先序遍历
- if(b != NULL)
- {
- ++top;
- path1[top] = b->data; //入栈
- if(b->data == s) //判断是否满足条件
- {
- cout<<"路径为:"<<endl;
- for(int loop1 = 0; loop1 <= top; ++loop1)
- cout<<path1[loop1]<<' ';
- cout<<endl;
- return;
- }
- AncestorPath(b->lchild,s);
- AncestorPath(b->rchild,s);
- --top; //恢复环境
- }
- }
- //此上下两个函数 先序 与 后序的区别是 先序是先判断是否满足条件在进行 遍历左子树, 后序则是先深度遍历左子树,在判断右子树的时候,才判断是否满足条件
- //不管怎么说都 在栈中存在的元素都是所求的元素的 祖先节点
- void AncestorPath2(BTNode *b, char s) //此函数同样是实现 根节点到值为s的节点之间路径输出,采用的方法是后序法,与求两个 节点 最近的祖先节点方法类似
- {
- BTNode *st[MAXSIZE], *temp, *temp2;
- int top = -1;
- bool flag = true;
- char path[MAXSIZE];
- if(b != NULL)
- {
- temp = b;
- do
- {
- while(temp != NULL)
- {
- ++top;
- st[top] = temp;
- temp = temp->lchild;
- }
- temp2 = NULL;
- flag = true;
- while(flag && top > -1)
- {
- temp = st[top];
- if(temp->rchild == temp2)
- {
- if(temp->data == s)
- {
- cout<<"根节点到 "<<s<<" 之间的路径为 :"<<endl;
- for(int loop1 = 0; loop1 <= top; ++loop1)
- cout<<st[loop1]->data<<' ';
- cout<<endl;
- return;
- }
- --top;
- temp2 = temp;
- }else
- {
- flag = false;
- temp = temp->rchild;
- }
- }
- }while(top > -1);
- }
- }
- char path3[MAXSIZE];
- int top2 = -1;
- void Link(BTNode *b) //先序找树中的叶子节点
- {
- if(b != NULL)
- {
- if(b->lchild == NULL && b->rchild == NULL)
- path3[++top2] = b->data;
- else
- {
- Link(b->lchild);
- Link(b->rchild);
- }
- }
- }
- void Link2(BTNode *b) //采用中序遍历 找叶子节点
- {
- if(b != NULL)
- {
- Link2(b->lchild);
- if(b->lchild == NULL && b->rchild == NULL)
- path3[++top2] = b->data;
- Link2(b->rchild);
- }
- }
- void Print(BTNode *b,int w) //把树向 逆时针旋转90度打印出来
- {
- if(b != NULL)
- {
- Print(b->rchild, w+5);
- for(int loop1 = 0; loop1 <= w; ++loop1)
- cout<<' ';
- cout<<b->data<<endl;
- Print(b->lchild,w+5);
- }
- }
- float ExpValue(BTNode *b) //此函数是计算一个树组成的表达式
- {
- if(b != NULL) //当其不为0的时候执行操作
- {
- switch(b->data)
- {
- case '+' :
- return ExpValue(b->lchild) + ExpValue(b->rchild); break;
- case '-' :
- return ExpValue(b->lchild) - ExpValue(b->rchild); break;
- case '*' :
- return ExpValue(b->lchild) * ExpValue(b->rchild); break;
- case '/' :
- return ExpValue(b->lchild) / ExpValue(b->rchild); break;
- default :
- return b->data - '0'; //此处目前只能是各位数,如果节点的值是数的话,就直接返回
- }
- }
- }
- bool bflag = false; //左子树的标记
- bool bflag2 = false; //右子树的标记
- void InorderExp(BTNode *b) //此函数是通过树求得其表达式, 思路是当节点的数据域是符号的时候,就把其与左节点的数据域比较,如果左节点的数据域是符号并且
- { //子节点的优先级比根节点的小,那么就输出‘(’并把标记设置为true, 当遍历右子树的时候仍然拿其与右子树的数据域比较如果,其符号的优先级比右子树的优先级大,
- if(b != NULL) //就输出一个‘(’然后设置标记为true
- {
- switch(b->data)
- {
- case '+' :
- case '-' :
- InorderExp(b->lchild); //表示先遍历左子树
- cout<<b->data<<' '; //输出其值
- InorderExp(b->rchild); break; //遍历右子树
- case '*' :
- case '/' : //当遇到 * /的时候就应当判断优先级了
- if(b->lchild != NULL) //此处是判断左子树
- {
- if(b->lchild->data == '+' || b->lchild->data == '-')
- { cout<<'(';
- bflag = true;
- }
- }
- InorderExp(b->lchild);
- if(bflag)
- { cout<<')'; bflag = false;}
- cout<<b->data<<' '; //此处才是输出 * 或者/ 的地方,也就是在 ‘(’‘)’都输出完成的时候,如果有的话
- if(b->rchild != NULL) //此处判断右子树
- {
- if(b->rchild->data == '+' || b->rchild->data == '-')
- { cout<<'(';
- bflag2 = true;
- }
- }
- InorderExp(b->rchild);
- if(bflag2)
- { cout<<')'; bflag2 = false; }
- break;
- default :
- cout<<b->data<<' ';
- break;
- }
- }
- }
- int process(char op1, char op2) //此函数是判断优先级
- {
- if(op1 != '+' && op1 != '-' && op1 != '*' && op1 != '/') return -1;
- if(op2 != '+' && op2 != '-' && op2 != '*' && op2 != '/') return -1;
- if(op1 == '+' || op1 == '-')
- {
- return 0;
- }
- if(op1 == '*' || op1 == '/')
- {
- if(op2 == '+' || op2 == '-')
- return 1;
- }
- }
- void InorderExp2(BTNode *b) //此函数也是求得表达式
- {
- int flag1 = 2;
- int flag2 = 2;
- if(b != NULL)
- {
- if(b->lchild != NULL)
- {
- flag = process(b->data,b->lchild->data);
- if(flag == 1) cout<<'(';
- InorderExp(b->lchild);
- if(flag == 1) cout<<')';
- }
- cout<<b->data;
- if(b->rchild != NULL)
- {
- flag2 = process(b->data,b->rchild->data);
- if(flag2 == 1) cout<<'(';
- InorderExp(b->rchild);
- if(flag2 == 1) cout<<')';
- }
- }
- }
- float value[MAXSIZE];
- int top22 = -1;
- char postExp[MAXSIZE];
- void PostExp(BTNode *b) //此函数的目的是把树转换为后缀表达式,怎么转换为后缀表达式呢??? 采用后序遍历即可
- { //因为符号位总是位于双亲结点,这样就能先把 数字位给进栈, 然后进栈符号位
- if(b != NULL)
- {
- PostExp(b->lchild);
- PostExp(b->rchild);
- ++top22;
- postExp[top22] = b->data;
- }
- }
- void CompValue(char postExp[]) //此函数是为了通过后缀表达式来求得其值
- { top = -1;
- char ch;
- int loop1 = 0;
- ch = postExp[loop1];
- float lValue = 0.0f, rValue = 0.0f, fValue = 0.0f;
- while(ch != '\0')
- {
- switch(ch)
- {
- case '+' :
- rValue = value[top];
- --top;
- lValue = value[top];
- fValue = lValue + rValue;
- value[top] = fValue;
- ch = postExp[++loop1];
- break;
- case '-' :
- rValue = value[top];
- --top;
- lValue = value[top];
- fValue = lValue - rValue;
- value[top] = fValue;
- ch = postExp[++loop1];
- break;
- case '*' :
- rValue = value[top];
- --top;
- lValue = value[top];
- fValue = lValue * rValue;
- value[top] = fValue;
- ch = postExp[++loop1];
- break;
- case '/' :
- rValue = value[top];
- --top;
- lValue = value[top];
- if(rValue == 0) { cout<<"发生除零错误。"<<endl; exit(1); }
- fValue = lValue / rValue;
- value[top] = fValue;
- ch = postExp[++loop1];
- break;
- default :
- fValue = float( ch - '0'); //因为数字位 是个位数,所以此处没有进行处理
- ++top;
- value[top] = fValue;
- ch = postExp[++loop1];
- break;
- }
- }
- if(top == 0)
- { cout<<"所求的值为:"<<value[top]<<endl; }
- else
- { cout<<"求值错误。"<<endl; }
- }
- void NodeToRoot(BTNode *b) //此函数是在结点中增加一个双亲域以后, 输出每个结点到根节点的路径
- { //采用先序遍历
- BTNode *temp;
- if(b != NULL)
- {
- temp = b;
- while(temp != NULL)
- {
- cout<<temp->data<<' ';
- temp = temp->parent;
- }
- cout<<endl;
- NodeToRoot(b->lchild);
- NodeToRoot(b->rchild);
- }
- }
- void PreToPost(ElemType pre[], int begin1, int end1, ElemType post[], int begin2, int end2)
- { //此函数将先序序列转换为后序序列, 因为先序序列的第一个元素是后序序列的最后一个元素, 然后就以此为条件进行递归操作
- int half = 0;
- if(end1 >= begin1)
- { half = (end1 - begin1 ) / 2; //half保存了中间的值
- post[end2] = pre[begin1];
- PreToPost(pre, begin1 + 1, begin1 + half, post, begin2,begin2 + half -1); //此处是遍历先序左子树, 要注意的是在使用half的时候,应该加上开始点, 其half是一个区间,不是一个点,加上begin开始点后,才表示中间点的位置
- PreToPost(pre, begin1 + half + 1, end1, post,begin2 + half, end2 -1); //此处是遍历先序右子树
- }
- }
- void CreateHT(HTNode ht[], int n) //通过数组构造一棵哈夫曼树, 因为一棵哈夫曼树有n个节点的话,就会有总共2n - 1 个节点
- {
- int loop1 = 0, loop2 = 0, loop3 = 0, lpos = 0, rpos = 0;
- float weight1 = 0.0f, weight2 = 0.0f; //用来保存权值,其中weight1 保存的是较小的
- for(loop1 =0; loop1 < n; ++loop1) //此循环把双亲 左右孩子节点域置为 -1;
- { ht[loop1].parent = -1;
- ht[loop1].lchild = -1;
- ht[loop1].rchild = -1;
- }
- for(loop1 = n; loop1 < 2 * n + 1; ++loop1) //由于数组前面的n个节点是叶子节点,所以此处处理 后面的 n -1 个非叶子节点
- {
- weight1 = 32767.0f;
- weight2 = 32767.0f;
- for(loop2 = 0; loop2 < loop1; ++loop2 )
- {
- if(ht[loop2].parent == -1) //只处理没有双亲节点的 节点
- {
- if(ht[loop2].weight < weight1) //保存最小的和 第二最小的权值及其位置
- {
- weight2 = weight1; rpos = lpos;
- weight1 = ht[loop2].weight; lpos = loop2;
- }
- else if(ht[loop2].weight < weight2)
- {
- weight2 = ht[loop2].weight;
- rpos = loop2;
- }
- }
- }
- ht[loop1].lchild = lpos; ht[loop1].rchild = rpos; ht[loop1].weight = ht[lpos].weight + ht[rpos].weight;
- ht[lpos].parent = loop1; ht[rpos].parent = loop1;
- }
- }
- //哈夫曼编码只能是叶子节点,并且哈夫曼树中只有度为 0 或2 的节点
- typedef struct
- {
- char cd[MAXSIZE]; //存储根节点到叶子节点的编码,即 0 或 1
- int start; //开始位置
- }HCode; //存储哈夫曼遍历的 节点结构
- /*
- void CreateHCode(HTNode ht[], HCode hcd[], int n) //此函数是根据哈夫曼树构造哈夫曼编码,思路是从每个叶子节点开始遍历到根节点, 如果节点是左节点 则表示为0 ,如果右节点否则表示为1
- {
- int loop1 = 0, parent;
- HCode hc;
- for(loop1 = 0; loop1 < n; ++loop1) //ht数组的前面n个元素是叶子节点, 所以遍历前面n个节点
- {
- hc.start = n;
- parent = ht[loop1].parent;
- while(parent != -1) //直到根节点 构造哈夫曼遍历
- {
- if(ht[parent].lchild == ht[loop1]) //如果是左节点的话,为 0
- hc.cd[hc.start--] = '0';
- else
- hc.cd[hc.start--] = '1'; //否则为右节点,那么就为 1
- parent = ht[parent].parent; //
- }
- ++hc.start;
- hcd[loop1] = hc;
- }
- }
- */
- typedef char ElemType;
- typedef struct hnode
- {
- int weight;
- ElemType data;
- struct hnode *lchild, *rchild;
- }HTree;
- typedef struct
- {
- ElemType data;
- int weight;
- }Node;
- typedef struct
- {
- char code[10];
- int weight;
- ElemType data;
- }NodeCode;
- struct cmp1
- {
- bool operator() (HTree *node1, HTree *node2)
- {
- return node1->weight >= node2->weight;
- }
- };
- void CreateHuffm(HTree *&root, Node nodes[], int num) //此函数借助priority_queue<> 构造哈夫曼树
- {
- int loop1 = 0, weight = 0;
- HTree *temp = NULL,*lchild = NULL, *rchild = NULL;
- priority_queue<HTree *, vector<HTree *>, cmp1> qp;
- for(loop1 = 0; loop1 < num; ++loop1)
- {
- temp = (HTree *)malloc(sizeof(HTree));
- temp->lchild = temp->rchild = NULL;
- temp->data = nodes[loop1].data;
- temp->weight = nodes[loop1].weight;
- qp.push(temp);
- }
- while(qp.size() != 1)
- {
- lchild = qp.top();
- qp.pop();
- rchild = qp.top();
- qp.pop();
- temp = (HTree *)malloc(sizeof(HTree));
- temp->lchild = lchild;
- temp->rchild = rchild;
- temp->weight = lchild->weight + rchild->weight;
- qp.push(temp);
- }
- root = qp.top();
- qp.pop();
- }
- void disp(HTree *tree)
- {
- if(tree != NULL)
- {
- cout<<tree->weight<<endl;
- disp(tree->lchild);
- disp(tree->rchild);
- }
- }
- NodeCode code[MAXSIZE];
- int loop = 0, looop = 0;
- char pathx[10];
- void HuffmCode(HTree *tree) //此函数求得每个节点的哈弗曼编码, 利用先序递归的方式
- {
- if(tree != NULL)
- {
- if(tree->lchild == NULL && tree->rchild == NULL)
- {
- code[looop].data = tree->data;
- code[looop].weight = tree->weight;
- strcpy(code[looop].code, pathx); //此步骤没用到loop的值,所以下面
- ++looop;
- }
- if(tree->lchild != NULL)
- {
- pathx[loop] = '0';
- ++loop;
- pathx[loop] = '\0'; // 应该加上'\0'
- HuffmCode(tree->lchild);
- }
- if(tree->rchild != NULL)
- {
- pathx[loop] = '1';
- ++loop;
- pathx[loop] = '\0';
- HuffmCode(tree->rchild);
- }
- --loop;
- pathx[loop] = '\0';
- }
- }