二叉树和其他树
可编译运行程序见:Github::Jasmine-up/Data-Structures-Algorithms-and-Applications/_23BinaryTree
定义
树
定义 11-1 一棵树 t是一个非空的有限元素的集合,其中一个元素为根(root),其余的元素(如果有的话)组成t的子树(subtree)。
在画一棵树时,每个元素都代表一个节点。树根画在上面,其子树画在下面。在根与子树的根(如果有子树)之间有一条边。同样的,每一棵子树也是根在上,其子树在下。在一棵树中,一个元素节点及其孩子节点之间用边连接。树不能为空。
树的另一常用术语为级(level)。树根是 1级,其孩子(如果有)是 2 级,孩子的孩子是3 级,等等。
一棵树的高度(height)或深度(depth)是树中级的个数。
一个元素的度(degree of an element)是指其孩子的个数。叶节点的度为0。
一棵树的度(degree of a tree)是其元素的度的最大值。
二叉树
定义
定义 11-2 一棵二叉树(binary tree)t 是有限个元素的集合(可以为空)。当二叉树非空时,其中有一个元素称为根,余下的元素(如果有的话)被划分成两棵二叉树,分别称为t的左子树和右子树。
二叉树和树的根本区别是:
树和二叉树的另一个区别大概与定义有关,二叉树可以为空,但树不能为空。有的作者放宽了对树的定义,允许树为空。
和树一样,二叉树也是根节点在顶部。二叉树左(右)子树中的元素画在根的左(右)下方。每个元素节点和其子节点之间用一条边相连。
二叉树特性
二叉树的特性:
证明 二叉树的每个元素(除了根节点)有且只有一个父节点。在子节点与父节点间有且只有一条边,因此边数为n-1。
证明 因为每一级最少有1个元素,因此元素的个数最少为h。每个元素最多有2个子节点,则第i层节点元素最多为
2
h
−
1
2^h-1
2h−1 个,i>0。当 h=0 时,元素的总数为 0,也就是
2
0
−
1
2^0-1
∑
i
=
1
h
2
h
−
1
=
2
h
−
1
sum_{i=1}^h2^{h-1}=2^h-1
∑i=1h2h−1=2h−1。
证明 因为每层至少有一个元素,因此高度不会超过n。由特性 11-2可以得知,高度为h 的二叉树最多有
2
h
−
1
2^h-1
2h−1个元素。因为 n<
2
h
−
1
2^h-1
2h−1,所以
h
>
l
o
g
2
(
n
+
1
)
h>log_2(n+1)
h>log2(n+1)。由于h是整数,所以
h
≥
⌈
l
o
g
2
(
n
−
1
)
⌉
h≥⌈log2(n−1)⌉。
满二叉树
当高度为h的二叉树恰好有
2
h
−
1
2^h-1
2h−1个元素时,称其为满二叉树(full binary tree)。如图11-6所示。
完全二叉树
对高度为h的满二叉树的元素,从第一层到最后一层,在每一次中从左至右,顺序编号,从1到
2
h
−
1
2^h-1
2h−1(如图11-6所示)。假设从满二叉树中删除k个其编号为
2
h
−
i
2^h-i
2h−i元素,
1
≤
i
≤
<
2
h
1≤i≤k<2^h
1≤i≤k<2h,所得到的二叉树被称为完全二叉树(complete binary tree)。满二叉树是完全二叉树的特例。
在完全二叉树中,一个元素与其孩子的编号有非常好的对应关系。
特性 11-4 设完全二叉树的一元素其编号为i,
1
<
i
<
n
1<i<n
1<i<n。有以下关系成立:
1)如果
i
=
1
i=1
i=1,则该元素为二叉树的根。若
i
>
1
i>1
i>1,则其父节点的编号为
⌊
i
/
2
⌋
lfloor i/2rfloor
⌊i/2⌋。
2)如果
2
i
>
n
2i>n
2i>n,则该元素无左孩子。否则,其左孩子的编号为
2
i
2i
2i。左孩子一定是偶数。
3)如果
2
i
+
1
>
n
2i+1>n
2i+1>n,则该元素无右孩子。否则,其右孩子的编号为
2
i
+
1
2i+1
2i+1。右孩子一定是奇数。
树的二叉树描述
当分布树t有节点超过两个孩子时,依然用二叉树来表示。此时,对每个节点x,可用其孩子节点的rightChild指针把x的所有孩子链成一条链表。x节点的leftChild指针指向该链表的第一个节点。x节点的rightChild指针来指向 x 的兄弟。图 11-15 是一棵树和其二叉树,其中实线表示指向左孩子的指针,虚线表示指向右孩子的指针。
将森林转换为二叉树
(1)把每个树转换为二叉树。
(2)第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连接起来。当所有的二叉树连接起来后就得到了由森林转换来的二叉树。
二叉树的描述
数组描述
链表描述
二叉树最常用的表示方法是用指针。每个元素用一个节点表示,节点有两个指针域,分别称为leftChild和rightChild。除两个指针域之外,每个节点还有一个element域。
父节点的每一个指向孩子的指针表示一条边。因为 n个元素的二叉树仅有 n-1 条边,所以有2n-(n-1)=n+1个指针域没有值,它们被置为NULL。图 11-10 是二叉树的链表表示。
二叉树遍历
-
前序遍历:根左孩子右孩子。对应于前缀表达式,在前缀(prefix)表达式中,操作符位于操作数之前,操作数从左到右顺序出现。不会存在歧义,无需括号和优先级。
-
中序遍历:左孩子根右孩子。对应于中缀表达式,每个二元操作符(即有两个操作数的操作符)出现在左操作数之后,右操作数之前。但是这种表达式有歧义,如
+
y
∗
z
x+y*z
x+y∗z是解释为
(
x
+
y
)
∗
z
(x+y)*z
(x+y)∗z还是
x
+
(
y
∗
z
)
x+(y*z)
-
后序遍历:左孩子右孩子根。对应于后缀表达式,在后缀(postfix)表达式中,每个操作符跟在操作数之后,操作数从左到右顺序出现。不会存在歧义,无需括号和优先级。
练习题
- 24.编写一个C++ 函数,复制一个数组表示的二叉树。
- 27.编写一个函数,计算一棵链式二叉树的高度。确定其时间复杂性。
- 29.编写一个函数,确定一棵链式二叉树在哪一层具有最多的节点(提示:用层次遍历)。确定其时间复杂性。
- 33.设有一棵二叉树,每个节点的数据都不相同。数据域的前序和中序序列是否可以唯一地确定这棵二叉树?如果可以,编写一个函数,用前序和中序序列来构造这棵二叉树。计算函数的时间复杂性。
- 34.按前序和后序遍历方法完成练习33。
- 35.按后序和中序遍历方法完成练习33。
- 36.编写一个C++函数,输入后缀表达式,构造其二叉树表示。假设每个操作符有一个或两个操作数。
- 37.用前缀表达式完成练习36。
- 38.编写一个 C++函数,把后缀表达式转换为完全括号化的中缀表达式。
- 40.编写一个函数,把一个中缀形式表达式(不一定是完全括号化的)转换成后缀表达式。假定操作符可以是二元操作符+、一、*、1,分界符可以是左括号()和右括号()。因为操作数的顺序在中缀、前缀、后缀表达式中都是一样的,所以在从中缀向前缀或后缀转换时,仅需要从左到右扫描中缀表达式。把扫描到的操作数直接输出,而遇到的操作符保留在栈中,根据操作符和左括号的优先级来确定输出。假定+和-的优先级为1,*和/的优先级为 2。栈外的左括号优先级为 3,栈内的左括号优先级为 0。
- 41.完成练习40,但是生成前缀表达式。
- 43.编写一个函数,计算后缀表达式的值。假设表达式以数组方式表示。
- 44.编写类linkedBinaryTree的一个复制构造函数。测试代码。计算时间复杂性。
- 45.编写方法linkedBinaryTree <E>::compare(x),比较二叉树*this与二叉树x。当且仅当它们相同时,返回 true。测试你的代码。计算时间复杂性。
- 46.编写方法linkedBinaryTree<E>::swapTrees(),它交换每一个节点的左右子树。测试你的代码。计算时间复杂性。
二叉树的应用
设置信号放大器
在一个分布式网络中,资源从生产地送往其他地方。例如,汽油或天然气经过管道网络,从生产基地送到消费地。同样的,电力也是通过电网从发电厂输送到各消费点。可以用术语信号(signal)来指称输送的资源(汽油、天然气、电力等)。当信号在网络中传输时,它在某一方面或某几个方面的性能可能会损失或衰减。例如,天然气管道中的气压会减少,电网上的电压会降低。另一方面,在信号传输中,噪声会增加。在信号从信号源到消费点传输的过程中,仅能容忍一定范围内的信号衰减。为了保证信号衰减不超过容忍值(tolerance),应在网络中至关重要的位置上放置信号放大器(signal booster)。信号放大器可以增加信号的压强或电压使它与源点相同;可以增强信号,使信号与噪声之比与源点的相同。本节将设计一个算法,以计算信号放大器的放置地点。目标是,放大器的数目最少,同时保证信号衰减(与源点信号相关)不超过给定的容忍值。
为简化问题,假设分布网络是一树形结构,源点是树的根。树的每一个非根节点表示一个可以放置放大器的地点,某些节点同时也表示消费点。信号从一个节点流向其子节点。图11-12 是一树形分布网络。每条边上的数字是信号从父节点流到其子节点的信号衰减量。衰减量的单位假设可以附加。在图 11-12中,信号从节点p流到节点v的衰减量是5。从节点q到节点x的衰减量为3。如果把一个信号放大器放在节点r,那么虽然当信号从节点p到达节点r时,它的强度比在节点p时衰减了3个单位,但是,当它从节点r流出时,又恢复了它在源点p的强度。因此,信号到达节点v时,强度比在源点p时衰减了2个单位,到达节点z时,强度比在源点p时衰减了 4个单位。如果在节点r没有信号放大器,那么在节点 z 的信号将衰减 7个单位。
设 degradeFromParent(i)表示节点i与其父节点间的衰减量。因此,在图 11-12 中,degradeFromParent(s)=2,degradeFromParent(p)=0,degradeFromParent(r)=3。因为信号放大器只能放在树形分布网的节点上,所以节点i的degradeFromParent(i)如果大于容忍值,那么即使在节点i放置了信号放大器,信号的衰减量依然要超过容忍值。例如,若容忍值为2,则在图11-12的节点r上即使有信号放大器,信号的衰减量也不会小于或等于2。
在以节点i为根的子树中,从节点i到子树的每个叶子都有一个衰减量,我们把这些衰减量的最大者记为degradeToLeaf(i)。若i是叶节点,则degradeToLeaf(i)=0。在图11-12中,当iE(w,x,t,y,z)时,degradeToLeaf(i)=0。对于其他节点,degradeToLeaf(i)可以用下面的方程式来计算:
因此,degradeToLeaf(s)=3。在此公式中,要计算节点的degradeToLeaf值,就要先计算其子节点的degradeToLeaf值,因此必须对树进行遍历。先访问子节点再访问父节点。在访问一个节点时,同时计算它的degradeToLeaf值。这种遍历方法是对后序遍历的一种自然扩充,其树的度大于 2。
按照上述方法,在计算 degradeToLeaf过程中,遇到一节点i,它有一子节点j满足
如果不在j节点放置放大器,那么从i节点到叶节点的信号衰减量将超过容忍值,即使在i节点处放置了放大器也是如此。例如在图 11-12 中,当计算degradeToLeaf(q)时,有
如果容忍值为 3,那么在 q 点或其祖先的任意一点放置放大器,都不能减少 q 与其后代间的衰减量。我们需要在s点放一个放大器,这时degradeToLeaf(g)=3。
测试中使用的是下面这棵树:测试时,输入的a的degradeToLeaf为0,degradeFromParent为1;输入的b的degradeToLeaf为0,degradeFromParent为2。
并查集
问题描述:有 n个元素从1 到 n编号。开始时,每一个元素在自己的类中,然后执行一系列find和combine操作。操作find(theElement)返回元素theElement所在类的唯一特征,而combine(a,b)把包含a和b的两个类合并。
∣
S
∣
∣S∣个节点的树,一个节点代表一个元素。任何一个元素可以作为根元素;剩余元素的任何子集可以作为根元素的孩子;再剩余元素的任何子素集可以作为根元素的孙子,等等。
举例:如图11-16,我们说,元素1、2、20、30 等属于以20为根的集合;元素 11、16、25、28 属于以16为根的集合;元素 15 属于以 15 为根的集合;元素 26 和 32 属于以 26 为根的集合。
求解策略:并查集问题的求解策略是,把每一个集合表示为一棵树。在查找时,我们把根元素作为集合标志符。因此,find(3)的返回值是20(见图11-16);find(1)的返回值是20;find(26)的返回值是26。因为每一个集合都有唯一的根,所以当且仅当i和j属于同一个集合时,有find(i)=find(j)。为了确定元素theElement属于哪一个集合,我们从元素theElement 的节点开始,沿着节点到其父节点向上移动,直到根节点为止。
在合并时,我们假设在调用语句unite(classA,classB)中,classA 和 classB分别是两个不同树集合的根(即classA classB)。为了把两个集合合并,我们让一棵树成为另一棵树的子树。例如,假设classA=16而classB=26(见图11-16),如果让 classA 成为 classB 的子树,那么结果如图11-17a所示;如果让classB成为classA的子树,那么结果如图 11-17b所示。
C++实现:查集问题的求解策略是模拟指针的一个极好的应用。这里需要树的链表描述,其中每个节点必须有一个parent域,但不必有孩子域。还需要直接访问节点。为找到含有元素10的集合,先要找到元素10的节点,然后沿着节点的parent指针找到根节点。如果n个节点的索引号从1到n(n是元素个数),且节点e表示元素e,那么很容易实现直接访问。每个parent域给出父节点的索引,因此parent域为整数类型。
图11-18就是采用这种方法来表示图 11-16的。节点内的数字是其parent域的值,节点外的数字是该节点的索引。索引同时也是该节点所表示的元素。根节点的 parent 域被置为 0。因为没有索引是 0 的节点,所以 parent为0 表示不指向任何节点(即为空链)。
性能分析:构造函数的时间性能是O(numberOfElements);查找函数find的时间性能是O(h),其中h是树的高度。合并函数unite的时间性能是O(1)。假设一个系列操作要执行u次合并和f次查找,一个系列操作的时间为O(fu)。
每次合并前都要执行两次查找(这些查找决定了要合并的树的根),此处可以优化,因为查找比较费时间,如果每次合并都查找的话就很占用时间。
合并函数的性能改进:在对根为 i和根为j的树进行合并操作时,利用重量规则或高度规则,可以提高并查集算法的性能。
**定义 11-3[重量规则]**若根为i的树的节点数少于根为 j的树的节点数,则将j作为i 的父节点。否则,将i作为j的父节点。
**定义 11-4[高度规则]**若根为 i 的树的高度小于根为j 的树的高度,则将j 作为 i 的父节点,否则,将i作为j的父节点。
使用重量队则的测试见代码:_28binaryTreeChains.cpp
**引理 11-1[重量规则引理]**假设从单元素集合出发,用重量规则进行合并操作。若以此方式构建一棵具有 p 个节点的树 t,则 t 的高度最多为
⌊
l
o
g
2
p
⌋
+
1
lfloor log_2prfloor+1
⌊log2p⌋+1。
若使用重量规则,合并和查找序列的代价(不包括初始化时间)为O(u+flogu) =O(flogu)(因为我们假定f>u)。
查找函数的性能改进:缩短从元素e到根的查找路径。这个方法利用了路径压缩(path compression)过程,这个过程的实现至少有3种不同的途径–路径紧缩(path compaction)、路径分割(path splitting)和路径对折(path halving)。
在路径紧缩中,从待查节点到根节点的路径上,所有节点的parent 指针都被改为指向根节点。以图 11-20 为例,当执行查找操作find(10)时,从10到根的路径有节点10、15和3。把这些节点的parent域改为 2,就得到图 11-21(节点 3 的指针本来就指向 2,因此其 parent域不必修改。但是为简化起见,程序还是对路径上的每个节点都进行了修改)。
路径紧缩代码
在路径分割中,从e节点到根节点的路径上,除根节点和其子节点之外,每个节点的parent指针都被改为指向各自的祖父。在图11-20中,路径分割从节点13开始,结果得到图11-22 的树。在路径分割时,只考虑从e到根节点的一条路径就够了。
在路径对折中,从e节点到根节点的路径上,除根节点和其子节点之外,每隔一个节点,其parent指针都被改为指向各自的祖父。在路径对折中,指针改变的个数仅为路径分割中的一半。同样,在路径对折中只考虑从e到根的一条路径就够了。在图11-20中,路径对折从节点13开始,结果得到图11-23的树。
仿真测试
main.cpp
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年8月13日17点38分
Last Version: V1.0
Descriptions: main()函数,控制运行所有的测试函数
*/
#include "_28binaryTreeChains.h"
int main()
{
binaryTreeChainsTest();
treeExpressionsTest();
treeApplicationTest();
return 0;
}
_28binaryTreeChains.h
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年8月27日09点44分
Last Version: V1.0
Descriptions: 用链表表示的二叉树.h
笔记:
1.静态函数指针初始化格式:void (*binaryTreeChains<E>::visit)(binaryTreeNode<E>*) = 0;
2.不能单独专门化成员模板函数,只能针对整个类专门化。
3.在模板函数中可以使用typeid()区别对待特定数据类型。
本程序注意事项:
1.所有关于前缀、后缀、中缀表达式的全部使用了char类型代表元素,char类型数组存储整个表达式
*/
#pragma once
#ifndef _BINARYTREECHAINS_H_
#define _BINARYTREECHAINS_H_
#include <iostream>
#include <vector>
#include <cstring>
#include <stack>
#include <queue>
#include "_1myExceptions.h"
#include "_28binaryTreeNode.h"
#include "_28binaryTree.h"
#include "_28booster.h"
#include "_28treeUnionFindNode.h"
using namespace std;
/*二叉树基础测试函数*/
void binaryTreeChainsTest();
/*二叉树树表达式测试函数*/
void treeExpressionsTest();
/*二叉树应用的测试函数*/
void treeApplicationTest();
template<class E>
class binaryTreeChains : public binaryTree<binaryTreeNode<E>>
{
public:
/*二叉树的基础成员函数*/
/*构造函数函数*/
binaryTreeChains() {
root = nullptr; treeSize = 0;
}
/*练习44:编写类linkedBinaryTree的一个复制构造函数。测试代码。*/
/* 计算时间复杂性。复制构造函数*/
binaryTreeChains(binaryTreeChains<E>& m) {
root = treeCreateTree(m.root);
}
/*练习题33和练习题35*/
/*构造函数---先序和中序遍历或后序和中序创建二叉树*/
/*flag == false时,是先序和中序遍历构建二叉树;flag == true时,是后序和中序构建二叉树*/
binaryTreeChains(E preOrPostOrder[], E inOrder[],int length,bool flag)
{
if(flag == false)
root = preInCreateTree(preOrPostOrder, inOrder, length);
else
root = postInCreateTree(preOrPostOrder, inOrder, length);
}
/*构造函数---前缀或后缀或中缀表达式创建二叉树*/
/*
练习37:当flag = 1时,前缀表达式创建二叉树
当flag = 2时,中缀表达式创建二叉树
练习36:当flag = 3时,后缀表达式创建二叉树
*/
binaryTreeChains(E expression[], int length,int flag)
{
switch (flag)
{
case 1:
root = preExprCreateTree(expression, length);
break;
case 2:
root = inExprCreateTree(expression, length);
break;
case 3:
root = postExprCreateTree(expression, length);
break;
}
}
/*析构函数*/
~binaryTreeChains() { erase(); }
/*当树为空时,返回true;否则,返回false*/
bool empty() const { return treeSize == 0; }
/*返回元素个数*/
int size() const { return treeSize; }
/*前序遍历二叉树,使用函数指针的目的是是的本函数可以实现多种目的*/
void preOrder(void(*theVisit)(binaryTreeNode<E>*))
{
visit = theVisit;
/*是因为递归,所以才要这样的*/
preOrder(root);/*这里调用的是成员函数,preOrder()*/
}
/*前序遍历---输出endl*/
void preOrderOutput() { preOrder(output); cout << endl; }
/*前序遍历---不使用递归而使用迭代函数*/
vector<E> iterativePreOrder();
/*中序遍历二叉树,使用函数指针的目的是是的本函数可以实现多种目的*/
void inOrder(void(*theVisit)(binaryTreeNode<E>*))
{
visit = theVisit;
/*是因为递归,所以才要这样的*/
inOrder(root);/*这里调用的是静态成员函数inOrder()*/
}
/*中序遍历---输出endl*/
void inOrderOutput() { inOrder(output); cout << endl; }
/*中序遍历---不使用递归而使用迭代函数*/
vector<E> iterativeInOrder();
/*后续遍历二叉树,使用函数指针的目的是是的本函数可以实现多种目的*/
void postOrder(void(*theVisit)(binaryTreeNode<E>*))
{
visit = theVisit;
/*是因为递归,所以才要这样的*/
postOrder(root);/*这里调用的是静态成员函数inOrder()*/
}
/*后序遍历---输出endl*/
void postOrderOutput() { postOrder(output); cout << endl; }
/*后序遍历---不使用递归而使用迭代函数*/
vector<E> iterativePostOrder();
/*层次遍历二叉树*/
void levelOrder(void (*theVisit)(binaryTreeNode<E>*));
/*层次遍历---输出endl*/
void levelOrderOutput() { levelOrder(output); cout << endl; }
/*清空二叉树 这里必须使用后序遍历 不然会出错*/
void erase()
{
postOrder(dispose);
root = nullptr;
treeSize = 0;
}
/*输入时为了将root根节点传递给createBiTree()函数*/
void input(void)
{
createBiTree(root);
}
/*是一个手动创建二叉树的函数,使用本函数得手动设置各节点之间的关系,见信号放大器应用的使用*/
/*将左数和右数合并为一个树(也就是this树)*/
void makeTree(const E& element, binaryTreeChains<E>&, binaryTreeChains<E>&);
/*练习45:比较二叉树*this和二叉树m*/
bool compare(binaryTreeChains<E>& m)
{
return compareTree(root, m.root);
}
/*练习46:交换每一个结点的左右子树*/
void swapTrees()
{
swapTrees(root);
}
/*练习27:计算二叉树高度*/
int height() const { return height(root); }
/*练习47:计算二叉树的最大高度差*/
int maxHeightDifference()
{
return maxHeightDifference(root);
}
/*练习29:计算二叉树在那一层具有最多的结点---返回值为结点最多的层*/
int layerMaxNumOfNode();
/*计算二叉树在在哪一层具有最多的结点--返回值为结点最多的层的结点数量*/
int maxNumOfNodeInLayer();
/*二叉树表达式的成员函数*/
/*计算树的表达式的值*/
int compulateTree()
{
return compulateTree(root);
}
private:
/*二叉树基础私有成员*/
binaryTreeNode<E>* root;//指向根的指针
int treeSize;//树的结点个数
static void (*visit)(binaryTreeNode<E>*);//是一个函数指针,返回值为void 函数参数为binaryTreeNode<E>*
static void preOrder(binaryTreeNode<E>* t);
static void inOrder(binaryTreeNode<E>* t);
static void postOrder(binaryTreeNode<E>* t);
static void dispose(binaryTreeNode<E>* t) { delete t; }
static void output(binaryTreeNode<E>* t) { cout << t->element << " "; }
/*创建二叉树---递归---作为私有成员只能被成员函数调用*/
void createBiTree(binaryTreeNode<E>*& tree);
/*复制构造函数调用的函数*/
binaryTreeNode<E>* treeCreateTree(binaryTreeNode<E>*& node);
/*私有成员函数---用于比较二叉树compare()*/
bool compareTree(binaryTreeNode<E>* thisNode, binaryTreeNode<E>* xNode);
/*私有成员函数---交换树的每个结点的左右子树---递归*/
void swapTrees(binaryTreeNode<E>*& node);
/*私有成员函数---计算二叉树高度---返回根为node的树的高度*/
int height(binaryTreeNode<E>* node) const;
/*私有成员函数---计算结点node的左右子树高度的差值*/
int heightDifference(binaryTreeNode<E>* node) const;
/*私有成员函数---计算二叉树的最大高度差---返回值为二叉树的最大高度差*/
int maxHeightDifference(binaryTreeNode<E>* node) const;
binaryTreeNode<E>* preInCreateTree(E preOrder[], E inOrder[], int size);
binaryTreeNode<E>* postInCreateTree(E postOrder[], E inOrder[], int size);
/*二叉树表达式的私有成员*/
/*计算树的表达式的值*/
/*本程序所有关于前缀、中缀、后缀表达式的处理全部是char类型,并且只能进行个位数的计算*/
int compulateTree(binaryTreeNode<E>* node) const;
binaryTreeNode<E>* preExprCreateTree(E expression[], int length);
binaryTreeNode<E>* inExprCreateTree(E expression[], int length);
binaryTreeNode<E>* postExprCreateTree(E expression[], int length);
};
/*私有静态成员初始化*/
/*这里是静态函数指针成员的初始化,不初始化会引发LINK错误*/
template<class E>
void (*binaryTreeChains<E>::visit)(binaryTreeNode<E>*) = 0; // visit function
/*非成员函数*/
/*返回元素data在数组中的位置,找到则返回该元素的位置,未找到则返回-1*/
template<class E>
int findRootLoc(E list[], E data, int length);
/*练习38:将后缀表达式转换为完全括号化的中缀表达式 完全括号化的中缀表达式如((a)+(b))*/
/*参数说明:postExpr是后缀表达式数组的指针,inExpr用于存储生成的前缀表达式,length是后缀表达式的长度*/
void postExprToIn(char postExpr[], string& inExpr, int length);
/*获取操作符的优先级,不包括栈外左括号的优先级,因为遇到栈外左括号,直接入栈就行*/
int getPriority(char op);
/*将中缀表达式转换为后缀表达式*/
/*参数说明:inExpr为指向中缀表达式的指针,postExpr为指向生成的后序表达式的指针,length表示中缀表达式的长度*/
void inExprToPost(char inExpr[], char postExpr[], int length);
/*练习41:将中缀表达式转换为前缀表达式---从后往前归入前缀表达式*/
/*参数说明:inExpr为指向中缀表达式的指针,preExpr为指向生成的后序表达式数组的指针,inLength表示中缀表达式的长度,preLength表示前缀表达式的长度*/
void inExprToPre(char inExpr[], char preExpr[], int inLength, int preLength);
/*练习39:将前缀表达式转换为带括号的中缀表达式 如((a+b)+(c*d))*/
/*参数说明:postExpr是后缀表达式数组的指针,inExpr用于存储生成的前缀表达式,length是后缀表达式的长度*/
void preExprToIn(char preExpr[], string& inExpr, int length);
/*计算表达式的值*/
/*前缀表达式计算表达式的值*/
/*参数说明:preExpr表示指向前缀表达式数组的指针,length表示前缀表达式的长度;返回值为计算的结果*/
int preCalculate(char preExpr[], int length);
/*中缀表达式计算表达式的值*/
/*参数说明:inExpr表示指向中缀表达式数组的指针,length表示中缀表达式的长度;返回值为计算的结果*/
int inCalculate(char inExpr[], int length);
/*练习43:后缀表达式计算表达式的值*/
/*参数说明:postExpr表示指向后缀表达式数组的指针,length表示后缀表达式的长度;返回值为计算的结果*/
int postCalculate(char postExpr[], int length);
/*二叉树的应用*/
/*设置信号放大器*/
void placeBoosters(binaryTreeNode<booster>* x);
/*并查集*/
void initialize(int numberOfElements);
int find(int theElement);
void unite(int rootA, int rootB);
/*二叉树的普通成员函数*/
/*前序遍历 递归*/
template<class E>
void binaryTreeChains<E>::preOrder(binaryTreeNode<E>* t)
{
if (t != nullptr)
{
visit(t);/*访问树根*/
preOrder(t->leftChild);/*前序遍历左子树*/
preOrder(t->rightChild);/*前序遍历右子树*/
}
}
/*前序遍历---不使用递归而使用迭代函数*/
template<class E>
vector<E> binaryTreeChains<E>::iterativePreOrder()
{
binaryTreeNode<E>* currentNode = root;
stack<binaryTreeNode<E>*> st;
vector<E> result;
/*写法1---前序中序后序遍历非递归统一版*/
/*首先将父节点入栈*/
if (currentNode != nullptr)
st.push(currentNode);
while (!st.empty())
{
currentNode = st.top();
st.pop();
/*如果遇到nullptr,则输出当前栈顶元素*/
if (currentNode == nullptr)
{
result.push_back(st.top()->element);
st.pop();
}
/*如果没有遇到nullptr,则按照右左中的顺序入栈结点,最后入栈nullptr*/
else
{
if (currentNode->rightChild != nullptr)
st.push(currentNode->rightChild);
if (currentNode->leftChild != nullptr)
st.push(currentNode->leftChild);
st.push(currentNode);
/*每次都在已遍历的根节点后入栈nullptr*/
st.push(nullptr);
}
}
///*写法2*/
///*当结点为nullptr并且栈为空时结束循环*/
//while (currentNode != nullptr || !st.empty())
//{
// /*先将左边的左边的元素入栈*/
// while (currentNode != nullptr)
// {
// st.push(currentNode);
// result.push_back(currentNode->element);
// currentNode = currentNode->leftChild;
// }
// /*然后一个一个遍历左边的元素,并将该元素存储到vector中*/
// currentNode = st.top();
// st.pop();
// currentNode = currentNode->rightChild;
//}
return result;
}
/*中序遍历 递归*/
template<class E>
void binaryTreeChains<E>::inOrder(binaryTreeNode<E>* t)
{
if (t != nullptr)
{
inOrder(t->leftChild);/*中序遍历左子树*/
visit(t);/*访问树根*/
inOrder(t->rightChild);/*中序遍历右子树*/
}
}
/*中序遍历---不使用递归而使用迭代函数*/
template<class E>
vector<E> binaryTreeChains<E>::iterativeInOrder()
{
binaryTreeNode<E>* currentNode = root;
stack<binaryTreeNode<E>*> st;
vector<E> result;
/*写法1---前序中序后序遍历非递归统一版*/
/*首先将父节点入栈*/
if (currentNode != nullptr)
st.push(currentNode);
while (!st.empty())
{
currentNode = st.top();
st.pop();
/*如果遇到nullptr,则输出当前栈顶元素*/
if (currentNode == nullptr)
{
result.push_back(st.top()->element);
st.pop();
}
/*如果没有遇到nullptr,则按照右左中的顺序入栈结点,最后入栈nullptr*/
else
{
if (currentNode->rightChild != nullptr)
st.push(currentNode->rightChild);
st.push(currentNode);
/*每次都在已遍历的根节点后入栈nullptr*/
st.push(nullptr);
if (currentNode->leftChild != nullptr)
st.push(currentNode->leftChild);
}
}
/*写法2*/
///*当结点为nullptr并且栈为空时结束循环*/
//while (currentNode != nullptr || !st.empty())
//{
// /*先将左边的左边的元素入栈*/
// while (currentNode != nullptr)
// {
// st.push(currentNode);
// currentNode = currentNode->leftChild;
// }
// /*然后一个一个遍历左边的元素,并将该元素存储到vector中*/
// currentNode = st.top();
// st.pop();
// result.push_back(currentNode->element);
// currentNode = currentNode->rightChild;
//}
return result;
}
/*后序遍历 递归*/
template<class E>
void binaryTreeChains<E>::postOrder(binaryTreeNode<E>* t)
{
if (t != nullptr)
{
postOrder(t->leftChild);/*后序遍历左子树*/
postOrder(t->rightChild);/*后序遍历右子树*/
visit(t);/*访问树根*/
}
}
/*后序遍历---不使用递归而使用迭代函数*/
template<class E>
vector<E> binaryTreeChains<E>::iterativePostOrder()
{
binaryTreeNode<E>* currentNode = root;
stack<binaryTreeNode<E>*> st;
vector<E> result;
/*前序中序后序遍历非递归统一版*/
/*首先将父节点入栈*/
if (currentNode != nullptr)
st.push(currentNode);
while (!st.empty())
{
currentNode = st.top();
st.pop();
/*如果遇到nullptr,则输出当前栈顶元素*/
if (currentNode == nullptr)
{
result.push_back(st.top()->element);
st.pop();
}
/*如果没有遇到nullptr,则按照右左中的顺序入栈结点,最后入栈nullptr*/
else
{
st.push(currentNode);
/*每次都在已遍历的根节点后入栈nullptr*/
st.push(nullptr);
if (currentNode->rightChild != nullptr)
st.push(currentNode->rightChild);
if (currentNode->leftChild != nullptr)
st.push(currentNode->leftChild);
}
}
return result;
}
/*层次遍历二叉树 非递归*/
template<class E>
void binaryTreeChains<E>::levelOrder(void (*theVisit)(binaryTreeNode<E>*))
{
visit = theVisit;
binaryTreeNode<E>* temp;
queue<binaryTreeNode<E>*> que;
que.push(root);
while (!que.empty())
{
temp = que.front();
que.pop();
visit(temp);
if (temp->leftChild != nullptr)
que.push(temp->leftChild);
if (temp->rightChild != nullptr)
que.push(temp->rightChild);
}
}
/*创建二叉树---递归---模板的实现*/
template<class E>
void binaryTreeChains<E>::createBiTree(binaryTreeNode<E>*& tree)
{
E data;
cout << "Please enter the tree element:";
while (!(cin >> data))
{
cin.clear();//清空标志位
while (cin.get() != 'n')//删除无效的输入
continue;
cout << "Please enter the tree element:";
}
cin.get();
/*针对char类型的特例*/
if (typeid(data) == typeid(char)) {
if (data == '#')
tree = nullptr;
else {
treeSize++;
tree = new binaryTreeNode<E>(data);
createBiTree(tree->leftChild);
createBiTree(tree->rightChild);
}
/*关于二叉树对于设置信号放大器的应用我新定义了成员函数maketree()生成二叉树
这里会报错:C2228“.degradeFromParent”的左边必须有类/结构/联合
我实在是不知道怎么改
*/
//else if (typeid(data) == typeid(booster))
// if (data.degradeFromParent == 999)
// tree = nullptr;
// else
// {
// treeSize++;
// tree = new binaryTreeNode<E>(data);
// createBiTree(tree->leftChild);
// createBiTree(tree->rightChild);
// }
}
else/*针对其他类型*/{
if (data == 999)
tree = nullptr;//当遇到999时,令树的根节点为nullptr,从而结束该分支的递归
else
{
treeSize++;
tree = new binaryTreeNode<E>(data);
createBiTree(tree->leftChild);
createBiTree(tree->rightChild);
}
}
}
/*是一个手动创建二叉树的函数,使用本函数得手动设置各节点之间的关系,见信号放大器应用的使用*/
/*将左树和右树合并为一个树*/
template<class E>
void binaryTreeChains<E>::makeTree(const E& element, binaryTreeChains<E>& left, binaryTreeChains<E>& right)
{// Combine left, right, and element to make new tree.
// left, right, and this must be different trees.
// create combined tree
root = new binaryTreeNode<E>(element, left.root, right.root);
treeSize = left.treeSize + right.treeSize + 1;
// deny access from trees left and right
left.root = right.root = NULL;
left.treeSize = right.treeSize = 0;
}
/*练习24:根据二叉树创建二叉树---用于复制构造函数*/
template<class E>
binaryTreeNode<E>* binaryTreeChains<E>::treeCreateTree(binaryTreeNode<E>*& node)
{
binaryTreeNode<E>* head = nullptr;
if (node != nullptr)
{
treeSize++;
// cout << "node->element = " << node->element << endl;
head = new binaryTreeNode<E>(node->element);
head->leftChild = treeCreateTree(node->leftChild);
head->rightChild = treeCreateTree(node->rightChild);
}
return head;
}
/*练习45:私有成员函数---用于比较二叉树compare()*/
template<class E>
bool binaryTreeChains<E>::compareTree(binaryTreeNode<E>* thisNode, binaryTreeNode<E>* xNode)
{
/*两个结点都为空时,二叉树相等*/
if (thisNode == nullptr && xNode == nullptr)
return true;
/*一个结点为空,一个结点非空,则二叉树不相等*/
if ((thisNode == nullptr && xNode != nullptr) || (thisNode != nullptr && xNode == nullptr))
return false;
/*两个结点的元素不等,则二叉树不相等*/
if (thisNode->element != xNode->element)
return false;
else/*两个结点相等,则比较彼此的左子树和右子树*/
return compareTree(thisNode->leftChild, xNode->leftChild) && compareTree(thisNode->rightChild, xNode->rightChild);
}
/*练习46:私有成员函数---交换树的每个结点的左右子树---递归*/
template<class E>
void binaryTreeChains<E>::swapTrees(binaryTreeNode<E>*& node)
{
if (node != nullptr)
{
swapTrees(node->leftChild);
swapTrees(node->rightChild);
binaryTreeNode<E>* temp = node->leftChild;
node->leftChild = node->rightChild;
node->rightChild = temp;
}
}
/*练习27:私有成员函数---计算二叉树高度---返回根为node的树的高度*/
template<class E>
int binaryTreeChains<E>::height(binaryTreeNode<E>* node) const
{
if (node == nullptr)
return 0;
int hl = height(node->leftChild);
int hr = height(node ->rightChild);
if (hl > hr)
return ++hl;
else
return ++hr;
}
/*私有成员函数---计算结点node的左右子树高度的差值*/
template<class E>
int binaryTreeChains<E>::heightDifference(binaryTreeNode<E>* node) const
{
if (node == nullptr)
return 0;
int lh = height(node->leftChild);
int rh = height(node->rightChild);
// cout << node->element << ":" << lh << endl;
// cout << node->element << ":" << rh << endl;
if (lh > rh)
return lh - rh;
else
return rh - lh;
}
/*练习47:私有成员函数---计算二叉树的最大高度差---返回值为二叉树的最大高度差*/
template<class E>
int binaryTreeChains<E>::maxHeightDifference(binaryTreeNode<E>* node) const
{
if (node == nullptr)
return 0;
int height = heightDifference(node);//当前结点的左右子树的高度差
int hl = maxHeightDifference(node->leftChild);//当前结点的左子树的左右子树的高度差
int hr = maxHeightDifference(node->rightChild);//当前结点的右子树的左右子树的高度差
if (height >= hl && height >= hr)
return height;
else if (hl >= height && hl >= hr)
return hl;
else if (hr >= height && hr >= hl)
return hr;
}
/*练习29:计算二叉树在那一层具有最多的结点---返回值为结点最多的层*/
/*当二叉树为空时,返回0*/
template<class E>
int binaryTreeChains<E>::layerMaxNumOfNode()
{
if (root == nullptr)
return 0;
int num = 0;//累加每层的结点数
int layer = 0;//记录当前的层数
int maxNum = 0;//存储结点最多的层的结点个数
int maxLayer = 0;//存储结点最多的层的层数
binaryTreeNode<E>* lastNode = root;//存储上一层最后一个结点的元素位置
binaryTreeNode<E>* nextNode = nullptr;//存储当前层最后一个结点的元素位置
binaryTreeNode<E>* currentNode;
queue<binaryTreeNode<E>*> que;
que.push(root);
while (!que.empty())
{
currentNode = que.front();
que.pop();
num++;
if (currentNode->leftChild != nullptr)
{
que.push(currentNode->leftChild);
nextNode = currentNode->leftChild;
}
if (currentNode->rightChild != nullptr)
{
que.push(currentNode->rightChild);
nextNode = currentNode->rightChild;
}
if (currentNode == lastNode)
{
layer++;//刚刚处理完第几层
lastNode = nextNode;
nextNode = nullptr;
if (num > maxNum)
{
maxNum = num;
maxLayer = layer;
}
num = 0;
}
}
return maxLayer;
}
/*计算二叉树在在哪一层具有最多的结点--返回值为结点最多的层的结点数量*/
/*当二叉树为空时,返回0*/
template<class E>
int binaryTreeChains<E>::maxNumOfNodeInLayer()
{
if (root == nullptr)
return 0;
int num = 0;//累加每层的结点数
int layer = 0;//记录当前的层数
int maxNum = 0;//存储结点最多的层的结点个数
int maxLayer = 0;//存储结点最多的层的层数
binaryTreeNode<E>* lastNode = root;//存储上一层最后一个结点的元素位置
binaryTreeNode<E>* nextNode = nullptr;//存储当前层最后一个结点的元素位置
binaryTreeNode<E>* currentNode = nullptr;
queue<binaryTreeNode<E>*> que;
que.push(root);
while (!que.empty())
{
currentNode = que.front();
que.pop();
num++;
if (currentNode->leftChild != nullptr)
{
que.push(currentNode->leftChild);
nextNode = currentNode->leftChild;
}
if (currentNode->rightChild != nullptr)
{
que.push(currentNode->rightChild);
nextNode = currentNode->rightChild;
}
if (currentNode == lastNode)
{
layer++;//刚刚处理完第几层
lastNode = nextNode;
nextNode = nullptr;
if (num > maxNum)
{
maxNum = num;
maxLayer = layer;
}
num = 0;
}
}
return maxNum;
}
/*使用前序和中序遍历构建二叉树*/
/*关键点在于找到根节点在中序中的位置,该位置之前为该根的左子树,该位置之后为该根的右子树*/
template<class E>
binaryTreeNode<E>* binaryTreeChains<E>::preInCreateTree(E preOrder[], E inOrder[], int size)
{
/*如果没有左右子树,则返回nullptr*/
if (size == 0)
return nullptr;
binaryTreeNode<E>* rootData = new binaryTreeNode<E>(preOrder[0]);
/*找到根节点的位置,中序中该位置左侧就是该根节点的左子树,该位置右侧就是该根节点的右子树*/
int rootLoc = findRootLoc<E>(inOrder, preOrder[0] ,size);
/*创建左子树和右子树*/
rootData->leftChild = preInCreateTree(preOrder + 1, inOrder, rootLoc);
rootData->rightChild = preInCreateTree(preOrder + 1 + rootLoc, inOrder + rootLoc + 1, size - 1 - rootLoc);
return rootData;
}
/*使用后序和中序遍历构建二叉树*/
/*关键点在于找到根节点在中序中的位置,该位置之前为该根的左子树,该位置之后为该根的右子树*/
template<class E>
binaryTreeNode<E>* binaryTreeChains<E>::postInCreateTree(E postOrder[], E inOrder[], int size)
{
/*如果没有左右子树,则返回nullptr*/
if (size == 0)
return nullptr;
binaryTreeNode<E>* rootData = new binaryTreeNode<E>(postOrder[size-1]);
/*找到根节点的位置,中序中该位置左侧就是该根节点的左子树,该位置右侧就是该根节点的右子树*/
int rootLoc = findRootLoc<E>(inOrder, postOrder[size-1], size);
/*创建左子树和右子树*/
rootData->leftChild = postInCreateTree(postOrder, inOrder, rootLoc);
rootData->rightChild = postInCreateTree(postOrder + rootLoc, inOrder + rootLoc + 1, size - 1 - rootLoc);
return rootData;
}
/*二叉树表达式的成员函数*/
/*计算树的表达式的值*/
/*用字符串记录表达式*/
/*这个函数需要使用char类型的树,其他类型的二叉树不满足要求*/
template<class E>
int binaryTreeChains<E>::compulateTree(binaryTreeNode<E>* node) const
{
if (node == nullptr)
return 0;
if (node->leftChild == nullptr && node->rightChild == nullptr) //左右子树都是nullptr时,说明它是叶子节点,而叶子结点就是数而非符号
return node->element - '0';//就返回叶子结点
int a = compulateTree(node->leftChild);//先计算左子树
int b = compulateTree(node->rightChild);//再计算右子树
switch (node->element)//当前结点不是叶子节点时,说明他是符号结点
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b != 0)
return a / b;
else
throw illegalParameterValue("除数不能为0!");
}
}
/*使用全部是二元操作符的前缀表达式创建二叉树*/
/*从尾元素开始遍历表达式的元素*/
/*如果是数据,则生成binaryTreeNode并入栈*/
/*如果不是数据,则生成binaryTreeNode,从栈中弹出两个数据形成其子树,第一个弹出的是其左子树,第二个弹出的是其右子树;然后再将当前结点入栈*/
template<class E>
binaryTreeNode<E>* binaryTreeChains<E>::preExprCreateTree(E expression[],int length)
{
stack<binaryTreeNode<E>*> st;//用于存储已经处理的数据生成的binaryTreeNode
binaryTreeNode<E>* temp = nullptr;
for (int i = length-1; i >= 0; i--)
{
/*如果是数据,则生成二叉树结点入栈*/
if (expression[i] >= '0' && expression[i] <= '9')
{
temp = new binaryTreeNode<E>(expression[i]);
st.push(temp);
}
else
{
temp = new binaryTreeNode<E>(expression[i]);
temp->leftChild = st.top();
st.pop();
temp->rightChild = st.top();
st.pop();
st.push(temp);
}
}
return temp;
}
/*使用全部是二元操作符的中缀表达式(包含括号以表明优先级)创建二叉树*/
/*如果是数据,则生成binaryTreeNode并入数据栈*/
/*
操作符处理规则:
如果当前操作符优先级大于操作符栈的顶部元素,直接入操作符栈
如果当前操作符优先级小于或等于操作符栈的顶部元素,先将顶部元素出操作符栈再将当前操作符入操作符栈
当前操作符为左括号时直接入栈
当前操作符为右括号时,让栈顶到左括号为止的操作符出操作符栈,括号不出现在后缀表达式中
出操作符栈时:生成当前符号的binaryTreeNode,其右子树为数据栈的栈顶元素,数据栈顶元素出栈,其左子树为数据栈当前的栈顶元素,数据栈顶元素出栈;
当前符号binaryTreeNode入数据栈。
*/
/*获取操作符优先级的getPriority()函数是一个非成员函数*/
template<class E>
binaryTreeNode<E>* binaryTreeChains<E>::inExprCreateTree(E expression[], int length)
{
stack<binaryTreeNode<E>*> st;//用于存储已经处理的数据生成的binaryTreeNode
stack<E> opStack;
binaryTreeNode<E>* temp = nullptr;
E data;
for (int i = 0; i < length; i++)
{
data = expression[i];
/*如果是数据,则生成二叉树结点入栈*/
if (data >= '0' && data <= '9')
{
temp = new binaryTreeNode<E>(data);
st.push(temp);
}
else
{
if (opStack.empty())
opStack.push(data);
else
switch (data)
{
case '(':opStack.push(data); break;//当遇到左括号时,直接将其入栈
case ')'://当遇到右括号时,让栈顶到左括号的操作符出栈
while (opStack.top() != '(')
{
temp = new binaryTreeNode<E>(opStack.top());
opStack.pop();
temp->rightChild = st.top();
st.pop();
temp->leftChild = st.top();
st.pop();
st.push(temp);
}
opStack.pop();//让(出栈
break;
/*当遇到+ - * /时,当其优先级大于栈顶元素时,入栈;否则,先将栈顶元素出栈,再将当前元素入栈*/
case '+':
case '-':
case '*':
case '/':
if (getPriority(data) > getPriority(opStack.top()))
opStack.push(data);
else
{
temp = new binaryTreeNode<E>(opStack.top());
opStack.pop();
temp->rightChild = st.top();
st.pop();
temp->leftChild = st.top();
st.pop();
st.push(temp);
}
break;
default:break;
}
/*当检查到中缀表达式的最后一个元素且栈非空时,将栈中的元素全部输出到后缀表达式*/
if (!opStack.empty() && i == length - 1)
while (!opStack.empty())
{
temp = new binaryTreeNode<E>(opStack.top());
opStack.pop();
temp->rightChild = st.top();
st.pop();
temp->leftChild = st.top();
st.pop();
st.push(temp);
}
}
}
return temp;
}
/*使用全部是二元操作符的后缀表达式创建二叉树*/
/*从首元素开始遍历表达式的元素*/
/*如果是数据,则生成binaryTreeNode并入栈*/
/*如果不是数据,则生成binaryTreeNode,从栈中弹出两个数据形成其子树,第一个弹出的是其右子树,第二个弹出的是其左子树;然后再将当前结点入栈*/
template<class E>
binaryTreeNode<E>* binaryTreeChains<E>::postExprCreateTree(E expression[], int length)
{
stack<binaryTreeNode<E>*> st;//用于存储已经处理的数据生成的binaryTreeNode
binaryTreeNode<E>* temp = nullptr;
for (int i = 0; i < length; i++)
{
/*如果是数据,则生成二叉树结点入栈*/
if (expression[i] >= '0' && expression[i] <= '9')
{
temp = new binaryTreeNode<E>(expression[i]);
st.push(temp);
}
else
{
temp = new binaryTreeNode<E>(expression[i]);
temp->rightChild = st.top();
st.pop();
temp->leftChild = st.top();
st.pop();
st.push(temp);
}
}
return temp;
}
#endif
_28binaryTreeChains.cpp
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年8月27日09点44分
Last Version: V1.0
Descriptions: 用链表表示的二叉树.cpp
笔记:
1.静态函数指针初始化格式:void (*binaryTreeChains<E>::visit)(binaryTreeNode<E>*) = 0;
2.不能单独专门化成员模板函数,只能针对整个类专门化。
3.在模板函数中可以使用typeid()区别对待特定数据类型。
本程序注意事项:
1.所有关于前缀、后缀、中缀表达式的全部使用了char类型代表元素,char类型数组存储整个表达式
*/
#include "_2myFunctions.h"
#include "_28binaryTreeChains.h"
/*二叉树应用的全局变量*/
/*设置信号放大器*/
int tolerance = 3;//设置容忍值为3
/*并查集*/
treeUnionFindNode* node;
void binaryTreeChainsTest()
{
cout << endl << "******************************binaryTreeChainsTest()函数开始**********************************" << endl;
cout << endl << "测试成员函数*******************************************" << endl;
cout << "输入****************************" << endl;
cout << "默认构造函数********************" << endl;
binaryTreeChains<int> a;
a.input();
cout << "a.preOrderOutput() = ";
a.preOrderOutput();
cout << "复制构造函数********************" << endl;
binaryTreeChains<int> b(a);
cout << "b.preOrderOutput() = ";
b.preOrderOutput();
cout << "前序中序构造函数****************" << endl;
int preOrder[] = { 1, 2, 4, 5, 3,7, 6 };
int inOrder[] = { 4, 2, 5, 1, 7, 3,6 };
binaryTreeChains<int>e(preOrder, inOrder, 7,false);
cout << "e.preOrderOutput() = ";
e.preOrderOutput();
cout << "e.inOrderOutput() = ";
e.inOrderOutput();
cout << "e.postOrderOutput() = ";
e.postOrderOutput();
cout << "后序中序构造函数****************" << endl;
int postOrder[] = { 4,5,2,7,6,3,1 };
binaryTreeChains<int>f(postOrder, inOrder, 7,true);
cout << "f.preOrderOutput() = ";
f.preOrderOutput();
cout << "f.inOrderOutput() = ";
f.inOrderOutput();
cout << "f.postOrderOutput() = ";
f.postOrderOutput();
cout << "输出****************************" << endl;
cout << "前序输出************************" << endl;
cout << "a.preOrderOutput() = ";
a.preOrderOutput();
cout << "b.preOrderOutput() = ";
b.preOrderOutput();
//迭代函数遍历
cout << "a.iterativePreOrder() = ";
vector<int> result = a.iterativePreOrder();
vector<int>::iterator ite;
ite = result.begin();
for (; ite != result.end(); ite++) {
cout << *ite << " ";
}
cout << endl;
cout << "中序输出************************" << endl;
//递归遍历
cout << "a.inOrderOutput() = ";
a.inOrderOutput();
cout << "b.inOrderOutput() = ";
b.inOrderOutput();
//迭代函数遍历
cout << "a.iterativeInOrder() = ";
result = a.iterativeInOrder();
ite = result.begin();
for (; ite != result.end(); ite++) {
cout << *ite << " ";
}
cout << endl;
cout << "后序输出************************" << endl;
cout << "a.postOrderOutput() = ";
a.postOrderOutput();
cout << "b.postOrderOutput() = ";
b.postOrderOutput();
//迭代函数遍历
cout << "a.iterativePostOrder() = ";
result = a.iterativePostOrder();
ite = result.begin();
for (; ite != result.end(); ite++) {
cout << *ite << " ";
}
cout << endl;
cout << "层序输出************************" << endl;
cout << "a.levelOrderOutput() = ";
a.levelOrderOutput();
cout << "b.levelOrderOutput() = ";
b.levelOrderOutput();
cout << endl;
cout << "empty()*************************" << endl;
cout << "a.empty() = " << a.empty() << endl;
cout << "b.empty() = " << b.empty() << endl;
cout << "size()**************************" << endl;
cout << "a.size() = " << a.size() << endl;
cout << "b.size() = " << b.size() << endl;
cout << "compare()***********************" << endl;
cout << "a.compare(b) = " << a.compare(b) << endl;
binaryTreeChains<int> c;
c.input();
cout << "a.compare(c) = " << a.compare(c) << endl;
cout << "swapTrees()*********************" << endl;
cout << "a.preOrderOutput() = ";
a.preOrderOutput();
a.swapTrees();
cout << "a.preOrderOutput() = ";
a.preOrderOutput();
cout << "height()************************" << endl;
cout << "a.height() = " << a.height() << endl;
cout << "maxHeightDifference()***********" << endl;
cout << "a.maxHeightDifference() = " << a.maxHeightDifference() << endl;
cout << "layerMaxNumOfNode()*************" << endl;
cout << "a.layerMaxNumOfNode() = " << a.layerMaxNumOfNode() << endl;
cout << "maxNumOfNodeInLayer()***********" << endl;
cout << "a.maxNumOfNodeInLayer() = " << a.maxNumOfNodeInLayer() << endl;
cout << "******************************binaryTreeChainsTest()函数结束**********************************" << endl;
}
void treeExpressionsTest()
{
cout << endl << "*******************************treeExpressionTest()函数开始***********************************" << endl;
cout << endl << "二叉树表达式测试***************************************" << endl;
/*使用前缀表达式创建二叉树*/
cout << "前缀表达式构造函数**************" << endl;
char preExpression[] = { '/','+','-','1','2','+','3','4','*','+','5','6','*','7','8'};
binaryTreeChains<char>g(preExpression, 15, 1);
cout << "g.preOrderOutput() = ";
g.preOrderOutput();
cout << "g.inOrderOutput() = ";
g.inOrderOutput();
cout << "g.postOrderOutput() = ";
g.postOrderOutput();
/*使用中缀表达式创建二叉树*/
cout << "中缀表达式构造函数**************" << endl;
char inExpression[] = { '(','(','1','-','2',')','+','(','3','+','4',')',')','/','(','(','5','+','6',')','*','(','7','*','8',')',')' };
/*这里的长度11是包含括号的长度*/
binaryTreeChains<char>i(inExpression, 27, 2);
cout << "i.preOrderOutput() = ";
i.preOrderOutput();
cout << "i.inOrderOutput() = ";
i.inOrderOutput();
cout << "i.postOrderOutput() = ";
i.postOrderOutput();
/*使用后缀表达式创建二叉树*/
cout << "后缀表达式构造函数**************" << endl;
char postExpression[] = { '1','2','-','3','4','+','+','5','6','+','7','8','*','*','/'};
binaryTreeChains<char>h(postExpression, 15, 3);
cout << "h.preOrderOutput() = ";
h.preOrderOutput();
cout << "h.inOrderOutput() = ";
h.inOrderOutput();
cout << "h.postOrderOutput() = ";
h.postOrderOutput();
/*计算树表达式的值*/
cout << "计算树表达式的值****************" << endl;
cout << "compulateTree()*****************" << endl;
binaryTreeChains<char> d;
d.input();
cout << "d.preOrderOutput() = ";
d.preOrderOutput();
cout << "d.compulateTree() = " << d.compulateTree() << endl;
/*将后缀表达式转换为中缀表达式*/
cout << "将后缀表达式转换为中缀表达式****" << endl;
cout << "postExprToIn()******************" << endl;
char postExpr1[9] = { '1','2','3','+','4','*','5','-','+' };
string inExpr1;
postExprToIn(postExpr1, inExpr1, 9);
cout << "postExpr1[9] = ";
traverse1dArray<char>(postExpr1, 9);
cout << "inExpr1 = " << inExpr1 << endl;
/*将前缀表达式转换为中缀表达式*/
cout << "将前缀表达式转换为中缀表达式****" << endl;
cout << "preExprToIn()*******************" << endl;
char preExpr1[9] = { '+','1','-','*','+','2','3','4','5' };
string inExpr2;
preExprToIn(preExpr1, inExpr2, 9);
cout << "preExpr1[9] = ";
traverse1dArray<char>(preExpr1, 9);
cout << "inExpr1 = " << inExpr2 << endl;
/*将中缀表达式转换为后缀表达式,需要注意的是,一定要使用括号标出优先级*/
cout << "将中缀表达式转换为后缀表达式****" << endl;
cout << "inExprToPost()******************" << endl;
char inExpr[25] = { '(','(','-','1',')','+','(','2','+','3',')',')','/','(','(','+','4',')','*','(','5','*','6',')',')' };
char postExpr[13];//这里的长度13是后缀表达式的长度
inExprToPost(inExpr, postExpr, 25);//这里的长度25是带有括号的中缀表达式的长度
cout << "inExpr[25] = ";
traverse1dArray<char>(inExpr, 25);
cout << "postExpr[13] = ";
traverse1dArray<char>(postExpr, 13);
cout << "将中缀表达式转换为前缀表达式****" << endl;
/*将中缀表达式转换为前缀表达式,需要注意的是,一定要使用括号标出优先级*/
cout << "inExprToPre()******************" << endl;
char preExpr[13];//这里的长度13是前缀表达式的长度
inExprToPre(inExpr, preExpr, 25,13);//这里的长度25是带有括号的中缀表达式的长度
cout << "inExpr[25] = ";
traverse1dArray<char>(inExpr, 25);
cout << "preExpr[13] = ";
traverse1dArray<char>(preExpr, 13);
/*计算表达式的值*/
/*计算前缀表达式的值*/
cout << "计算前缀表达式的值**************" << endl;
cout << "preCalculate()******************" << endl;
char preExpr2[9] = { '+','1','-','*','+','2','3','4','5' };
int result = preCalculate(preExpr2, 9);
cout << "result = " << result << endl;
/*计算中缀表达式的值*/
cout << "计算中缀表达式的值**************" << endl;
cout << "inCalculate()******************" << endl;
char inExpr3[17] = {'(','1','+','(','(','(','2','+','3',')','*','4',')','-','5',')',')'};
result = inCalculate(inExpr3, 17);
cout << "result = " << result << endl;
char inExpr4[27] = { '(','(','5','-','2',')','+','(','3','+','4',')',')','/','(' ,'(','1','+','1',')','*','(','1','*','1',')',')' };
result = inCalculate(inExpr4, 27);
cout << "result = " << result << endl;
/*练习43:计算后缀表达式的值*/
cout << "计算后缀表达式的值**************" << endl;
cout << "postCalculate()*****************" << endl;
char postExpr2[9] = { '1','2','3','+','4','*','5','-','+' };
result = postCalculate(postExpr2, 9);
cout << "result = " << result << endl;
cout << "*******************************treeExpressionTest()函数结束***********************************" << endl;
}
void treeApplicationTest()
{
cout << endl << "******************************treeApplicationTest()函数开始***********************************" << endl;
cout << endl << "二叉树应用测试*****************************************" << endl;
cout << "设置信号放大器******************" << endl;
booster a, b;
/*输入*/
cin >> a;// 这里输入0和1
cin >> b;// 这里输入0和2
binaryTreeChains<booster> t, u, v, w, x, y;
u.makeTree(a, x, x);
v.makeTree(b, u, x);
u.makeTree(a, x, x);
w.makeTree(a, u, x);
b.degradeFromParent = 3;
u.makeTree(b, v, w);
v.makeTree(a, x, x);
b.degradeFromParent = 3;
w.makeTree(b, x, x);
y.makeTree(a, v, w);
w.makeTree(a, x, x);
t.makeTree(b, y, w);
b.degradeFromParent = 0;
v.makeTree(b, t, u);
v.postOrder(placeBoosters);
v.postOrderOutput();
v.preOrderOutput();
v.inOrderOutput();
cout << "并查集**************************" << endl;
initialize(10);
unite(1, 2);
unite(3, 4);
unite(1, 3);
cout << "find(1) = " << find(1) << " find(2) = " << find(2) << endl;
cout << "find(3) = " << find(3) << " find(4) = " << find(4) << endl;
cout << "find(5) = " << find(5) << " find(6) = " << find(6) << endl;
cout << "******************************treeApplicationTest()函数结束***********************************" << endl;
}
/*非成员函数*/
/*返回元素data在数组中的位置,找到则返回该元素的位置,未找到则返回-1*/
template<class E>
int findRootLoc(E list[], E data, int length)
{
for (int i = 0; i < length; i++)
{
if (list[i] == data)
return i;
}
return -1;
}
/*获取优先级---inExprToPost中使用*/
int getPriority(char op)
{
switch (op)
{
case '+':return 1;
case '-':return 1;
case '*':return 2;
case '/':return 2;
case'(':return 0;
case ')':return 0;//右括号的优先级是0
default:break;
}
}
/*练习38:将后缀表达式转换为完全括号化的中缀表达式 完全括号化的中缀表达式如((a)+(b))*/
/*参数说明:postExpr是后缀表达式数组的指针,inExpr用于存储生成的前缀表达式,length是后缀表达式的长度*/
void postExprToIn(char postExpr[], string& inExpr, int length)
{
inExpr.clear();
stack<string> st;/*用于存储中间结果*/
string a, b, c;
char data;
for (int i = 0; i < length; i++)
{
data = postExpr[i];
/*当遇到数字时,直接入栈*/
if (data >= '0' && data <= '9')
st.push({ data,'' });
else
{
/*每执行一次计算就添加括号,每个数据都加括号实在不需要,看起来很冗余*/
b = st.top() + '';
st.pop();
a = st.top() + '';
st.pop();
c = '(' + a + data + b + ')';
st.push(c);
}
}
if (st.size() == 1)
inExpr = st.top();
else
inExpr = "后缀表达式非法,转换失败!";
}
/*练习40:将中缀表达式转换为后缀表达式*/
/*条件:必须要包含括号---用于区分优先级*/
/*操作数处理规则:因为操作数的顺序在中缀、前缀、后缀表达式中是一样的,所以在从中缀向前缀或后缀转换时,仅需要从左到右扫描中缀表达式*/
/*
操作符处理规则:
如果当前操作符优先级大于操作符栈的顶部元素,直接入栈
如果当前操作符优先级小于或等于操作符栈的顶部元素,先将顶部元素出栈再将当前操作符入栈
当前操作符为左括号时直接入栈
当前操作符为右括号时,让栈顶到左括号为止的操作符出栈,括号不出现在后缀表达式中
*/
/*把扫描到的操作数直接输出,而遇到的操作符保留在栈中,根据操作符和左括号的优先级来确定输出*/
/*+-的优先级为1,*和/的优先级为2,栈外左括号的优先级为3,栈内左括号的优先级为0*/
/*返回后缀表达式---使用postExpr[]数组返回*/
/*参数说明:inExpr为指向中缀表达式的指针,postExpr为指向生成的后序表达式的指针,length表示中缀表达式的长度*/
void inExprToPost(char inExpr[], char postExpr[], int length)
{
int postLoc = 0;//记录后缀表达式的当前位置
char data = 0;//记录读取的前缀表达式的元素
stack<char> opStack;//用于存储中间过程的符号
/*顺序访问中缀表达式*/
for (int i = 0; i < length; i++)
{
data = inExpr[i];
/*当遇到数字时,直接将其存储到后缀表达式中*/
if (data >= '0' && data <= '9')
{
postExpr[postLoc] = data;
postLoc++;//后缀表达式存储一个值,当前位置+1
}
else/*当遇到的是符号时*/
{
if (opStack.empty())
opStack.push(data);
else
switch (data)
{
case '(':opStack.push('('); break;//当遇到左括号时,直接将其入栈
case ')'://当遇到右括号时,让栈顶到左括号的操作符出栈
while (opStack.top() != '(')
{
postExpr[postLoc] = opStack.top();
postLoc++;
opStack.pop();
}
opStack.pop();//让(出栈
break;
/*当遇到+ - * /时,当其优先级大于栈顶元素时,入栈;否则,先将栈顶元素出栈,再将当前元素入栈*/
case '+':
case '-':
case '*':
case '/':
if (getPriority(data) > getPriority(opStack.top()))
opStack.push(data);
else
{
postExpr[postLoc] = opStack.top();
postLoc++;
opStack.pop();
opStack.push(data);
}
break;
default:break;
}
/*当检查到中缀表达式的最后一个元素且栈非空时,将栈中的元素全部输出到后缀表达式*/
if (!opStack.empty() && i == length - 1)
while (!opStack.empty())
{
postExpr[postLoc] = opStack.top();
postLoc++;
opStack.pop();
}
}
}
}
/*练习41:将中缀表达式转换为前缀表达式---从后往前归入前缀表达式*/
/*
首先设定一个操作符栈,从右到左顺序扫描整个中缀表达式,如果是操作数,则直接归入前缀表达式;
如果是操作符:
1.如果是右括号,则直接将其入栈;
2.如果是左括号,则将操作符栈中的操作符依次弹栈,归入前缀表达式,直至遇到右括号,将右括号弹栈,处理结束;
3.如果是其他操作符,如果栈顶操作符优先级大于当前操作符的优先级,则弹栈,并归入前缀表达式,
直至栈顶操作符优先级小于等于当前操作符优先级,这时将当前操作符压栈。
*/
/*参数说明:inExpr为指向中缀表达式的指针,preExpr为指向生成的后序表达式数组的指针,inLength表示中缀表达式的长度,preLength表示前缀表达式的长度*/
void inExprToPre(char inExpr[], char preExpr[], int inLength, int preLength)
{
int postLoc = preLength - 1;//记录前缀表达式的当前位置
char data = 0;//记录读取的中缀表达式的元素
stack<char> opStack;//用于存储中间过程的符号
/*顺序访问中缀表达式*/
for (int i = inLength - 1; i >= 0; i--)
{
data = inExpr[i];
/*当遇到数字时,直接将其存储到前缀表达式中*/
if (data >= '0' && data <= '9')
{
preExpr[postLoc] = data;
postLoc--;//前缀表达式存储一个值,当前位置-1
}
else/*当遇到的是符号时*/
{
/*当操作符栈为空时,直接将当前操作符入栈*/
if (opStack.empty())
opStack.push(data);
else
switch (data)
{
case ')':opStack.push(')'); break;//当遇到右括号时,直接将其入栈
case '('://当遇到左括号时,让栈顶到右括号的操作符出栈
while (opStack.top() != ')')
{
preExpr[postLoc] = opStack.top();
postLoc--;
opStack.pop();
}
opStack.pop();//让')'出栈
break;
/*当遇到+ - * /时,当栈顶元素优先级大于当前操作符优先级时,入栈;
否则,将栈顶元素出栈,出栈直到当前操作符优先级大于栈顶元素优先级,最后将当前元素入栈*/
case '+':
case '-':
case '*':
case '/':
while (getPriority(opStack.top()) > getPriority(data))/*栈顶元素操作符优先级大于当前操作符优先级,就出栈*/
{
preExpr[postLoc] = opStack.top();
postLoc--;
opStack.pop();
}
opStack.push(data);/*直到上述条件不成立,就入栈*/
break;
default:break;
}
/*当检查到中缀表达式的最后一个元素且栈非空时,将栈中的元素全部输出到前缀表达式*/
if (!opStack.empty() && i == 0)
while (!opStack.empty())
{
preExpr[postLoc] = opStack.top();
postLoc--;
opStack.pop();
}
}
}
}
/*练习39:将前缀表达式转换为带括号的中缀表达式 如((a+b)+(c*d))*/
/*参数说明:postExpr是后缀表达式数组的指针,inExpr用于存储生成的前缀表达式,length是后缀表达式的长度*/
void preExprToIn(char preExpr[], string& inExpr, int length)
{
inExpr.clear();
stack<string> st;/*用于存储中间结果*/
string a, b, c;
char data;
for (int i = length-1; i >= 0; i--)
{
data = preExpr[i];
/*当遇到数字时,直接入栈*/
if (data >= '0' && data <= '9')
st.push({ data,'' });
else
{
/*每执行一次计算就添加括号,每个数据都加括号实在不需要,看起来很冗余*/
a = st.top() + '';
st.pop();
b = st.top() + '';
st.pop();
c = '(' + a + data + b + ')';
st.push(c);
}
}
if (st.size() == 1)
inExpr = st.top();
else
inExpr = "后缀表达式非法,转换失败!";
}
/*计算表达式的值*/
/*前缀表达式计算表达式的值*/
/*参数说明:preExpr表示指向前缀表达式数组的指针,length表示前缀表达式的长度;返回值为计算的结果*/
int preCalculate(char preExpr[], int length)
{
stack<int> st;/*用于存储中间结果*/
char data;
int a, b;
for (int i = length - 1; i >= 0; i--)
{
data = preExpr[i];
/*当遇到数字时,直接入栈*/
if (data >= '0' && data <= '9')
st.push(data-'0');
else
{
switch (data)//当前结点不是叶子节点时,说明他是符号结点
{
case '+':
a = st.top();
st.pop();
b = st.top();
st.pop();
st.push(a + b);
break;
case '-':
a = st.top();
st.pop();
b = st.top();
st.pop();
st.push(a - b);
break;
case '*':
a = st.top();
st.pop();
b = st.top();
st.pop();
st.push(a * b);
break;
case '/':
a = st.top();
st.pop();
b = st.top();
st.pop();
if (b != 0)
st.push(a / b);
else
throw illegalParameterValue("除数不能为0!");
break;
default:break;
}
}
}
if (st.size() == 1)
return st.top();
else
cout << "前缀表达式非法,计算失败!" << endl;
return 0;
}
/*针对不同的操作符,执行不同的运算*/
/*参数说明:op即为操作符,a是左操作数,b是右操作数;返回值为运算的结果*/
int calculate(char op, int a, int b)
{
switch (op)
{
case '+':return a + b;
case '-':return a - b;
case '*':return a * b;
case '/':
if (b == 0)
throw illegalParameterValue("除数不能为0!");
else
return a / b;
}
}
/*中缀表达式计算表达式的值*/
/*参数说明:inExpr表示指向中缀表达式数组的指针,length表示中缀表达式的长度;返回值为计算的结果*/
int inCalculate(char inExpr[], int length)
{
stack<int> st;/*用于存储中间结果*/
stack<char> opStack;/*用于存储操作符*/
char data;
int a, b;
for (int i = 0; i < length; i++)
{
data = inExpr[i];
/*如果是数据,则生成二叉树结点入栈*/
if (data >= '0' && data <= '9')
st.push(data - '0');
else
{
if (opStack.empty())
opStack.push(data);
else
switch (data)
{
case '(':opStack.push(data); break;//当遇到左括号时,直接将其入栈
case ')'://当遇到右括号时,让栈顶到左括号的操作符出栈
while (opStack.top() != '(')
{
/*获取数据*/
b = st.top();
st.pop();
a = st.top();
st.pop();
/*获取符号*/
data = opStack.top();
opStack.pop();
/*计算结果并入栈*/
st.push(calculate(data, a, b));
}
opStack.pop();//让(出栈
break;
/*当遇到+ - * /时,当其优先级大于栈顶元素时,入栈;否则,先将栈顶元素出栈,再将当前元素入栈*/
case '+':
case '-':
case '*':
case '/':
if (getPriority(data) > getPriority(opStack.top()))
opStack.push(data);
else
{
b = st.top();
st.pop();
a = st.top();
st.pop();
st.push(calculate(data, a, b));
}
break;
default:break;
}
}
/*当检查到中缀表达式的最后一个元素且栈非空时,将操作符栈中的元素弹出并计算运算的值*/
if (!opStack.empty() && i == length - 1)
while (!opStack.empty())
{
/*获取数据*/
b = st.top();
st.pop();
a = st.top();
st.pop();
/*获取符号*/
data = opStack.top();
opStack.pop();
/*计算结果并入栈*/
st.push(calculate(data, a, b));
}
}
return st.top();
}
/*练习43:后缀表达式计算表达式的值*/
/*参数说明:postExpr表示指向后缀表达式数组的指针,length表示后缀表达式的长度;返回值为计算的结果*/
int postCalculate(char postExpr[], int length)
{
stack<int> st;/*用于存储中间结果*/
char data;
int a, b;
for (int i = 0; i < length; i++)
{
data = postExpr[i];
/*当遇到数字时,直接入栈*/
if (data >= '0' && data <= '9')
st.push(data - '0');
else
{
switch (data)//当前结点不是叶子节点时,说明他是符号结点
{
case '+':
b = st.top();
st.pop();
a = st.top();
st.pop();
st.push(a + b);
break;
case '-':
b = st.top();
st.pop();
a = st.top();
st.pop();
st.push(a - b);
break;
case '*':
b = st.top();
st.pop();
a = st.top();
st.pop();
st.push(a * b);
break;
case '/':
b = st.top();
st.pop();
a = st.top();
st.pop();
if (b != 0)
st.push(a / b);
else
throw illegalParameterValue("除数不能为0!");
break;
default:break;
}
}
}
if (st.size() == 1)
return st.top();
else
cout << "后缀表达式非法,计算失败!" << endl;
return 0;
}
/*二叉树的应用---设置信号放大器*/
void placeBoosters(binaryTreeNode<booster>* x)
{//计算节点x的衰减值,如果衰减值超过容忍值,则放置放大器
x->element.degradeToLeaf = 0; //初始化节点x的 到叶子节点的衰减值为0
//计算x的左子树的衰减值,根据需求放置放大器
//为什么在设置左右子树到叶子的衰减值时方法不一样,原因是在设置左子树时没有管其右子树到叶子的衰减值是否更大;
//但是呢每个根节点都有左子树和右子树,该根节点到叶子的衰减值设置为左右子树的最大衰减值;因此在设置右子树时
//就必须考虑左子树到叶子的衰减是否大于右子树
binaryTreeNode<booster>* y = x->leftChild;
if (y != nullptr)
{//如果x的左子树为空,则不管
//计算x到叶子节点的衰减值
int degradation = y->element.degradeToLeaf + y->element.degradeFromParent;
//如果衰减值大于容忍值
if (degradation > tolerance)
{//则在x的左子树放置放大器
y->element.boosterHere = true;
x->element.degradeToLeaf = y->element.degradeFromParent;
}
else //不需要放大器
x->element.degradeToLeaf = degradation;//将x到叶子节点的衰减值设置为degradation
}
//计算节点x的衰减值,如果衰减值超过容忍值,则放置放大器
y = x->rightChild;
if (y != nullptr)
{ //当x的右子树不为空时
int degradation = y->element.degradeToLeaf + y->element.degradeFromParent;
if (degradation > tolerance)
{//在x的右子树放置放大器
y->element.boosterHere = true;
degradation = y->element.degradeFromParent;
}
if (x->element.degradeToLeaf < degradation)
x->element.degradeToLeaf = degradation;
}
}
/*二叉树的应用---并查集*/
void initialize(int numberOfElements)
{//初始化numberOfElements个树,每个树都是一个元素
node = new treeUnionFindNode[numberOfElements + 1];
}
int find(int theElement)
{
//返回元素theElement所在树的根
while (!node[theElement].root)//只要节点的root不为true,就一直找他的父节点
theElement = node[theElement].parent; // move up one level
return theElement;
}
/*rootA和rootB是索引*/
void unite(int rootA, int rootB)
{
//使用重量规则,合并其根不同的树
//重量大的作为根节点
if (node[rootA].parent < node[rootB].parent)
{
node[rootB].parent += node[rootA].parent;
node[rootA].parent = rootB;
node[rootA].root = false;
}
else
{
node[rootA].parent += node[rootB].parent;
node[rootB].parent = rootA;
node[rootB].root = false;
}
}
_28binaryTreeNode.h
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年8月27日09点44分
Last Version: V1.0
Descriptions: 二叉树的结点结构体
*/
#pragma once
#ifndef _BINARYTREENODE_H_
#define _BINARYTREENODE_H_
template<class T>
struct binaryTreeNode
{
T element;
binaryTreeNode<T>* leftChild,//左子树
*rightChild;//右子树
/*默认构造函数*/
binaryTreeNode() { leftChild = rightChild = nullptr; }
/*只初始化element*/
binaryTreeNode(T melement)
{
element = melement;
leftChild = rightChild = nullptr;
}
/*三个元素都初始化*/
binaryTreeNode(T melement, binaryTreeNode<T>* mleftChild, binaryTreeNode<T>* mrightChild)
{
element = melement;
leftChild = mleftChild;
rightChild = mrightChild;
}
};
#endif
_28binaryTree.h
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年8月27日09点43分
Last Version: V1.0
Descriptions: 二叉树的抽象类
*/
#pragma once
#ifndef _BINARYTREE_H_
#define _BINARYTREE_H_
template<class T>
class binaryTree
{
public:
virtual ~binaryTree() {}
virtual bool empty() const = 0;
virtual int size() const = 0;
virtual void preOrder(void (*)(T*)) = 0;
virtual void inOrder(void (*)(T*)) = 0;
virtual void postOrder(void (*)(T*)) = 0;
virtual void levelOrder(void (*)(T*)) = 0;
};
#endif
_28booster.h
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年9月17日10点47分
Last Version: V1.0
Descriptions: 二叉树的应用 设置信号放大器 节点结构体
*/
#pragma once
#ifndef _BOOSTER_H_
#define _BOOSTER_H_
#include <iostream>
using namespace std;
struct booster
{
int degradeToLeaf, //到叶子结点的衰减
degradeFromParent; //父节点到该节点的衰减
bool boosterHere; //是否在此节点放置放大器
/*重载<<操作符*/
friend ostream& operator<<(ostream& out, booster& m)
{
out << m.boosterHere << ' ' << m.degradeToLeaf << ' ' << m.degradeFromParent << ' ';
return out;
}
/*重载>>操作符*/
friend istream& operator>>(istream& in, booster& m)
{
cout << "Please enter the degradeToLeaf:";
while (!(in >> m.degradeToLeaf))
{
in.clear();//清空标志位
while (in.get() != 'n')//删除无效的输入
continue;
cout << "Please enter the degradeToLeaf:";
}
cout << "Please enter the degradeFromParent:";
while (!(in >> m.degradeFromParent))
{
in.clear();//清空标志位
while (in.get() != 'n')//删除无效的输入
continue;
cout << "Please enter the degradeFromParent:";
}
m.boosterHere = 0;
return in;
}
};
#endif
_28treeUnionFindNode.h
#pragma once
#ifndef _TREEUNIONFINDNODE_H_
#define _TREEUNIONFINDNODE_H_
/*
注意事项:
1.当且仅当一个节点是当前根节点时,他的root域为true;
2.每个根节点的parent域用来记录该树的节点总数
规则
1.重量规则
若根为i的树的节点数少于根为j的树的节点数,则将j作为i的父节点;否则,将i作为j的父节点。
2.高度规则
若根为i的树的高度小于根为j的树的高度,则将j作为i的父节点;否则,将i作为j的父节点。
*/
struct treeUnionFindNode
{
int parent; // if true then tree weight
// otherwise pointer to parent in tree
bool root; // true iff root
treeUnionFindNode()
{
parent = 1; root = true;
}
};
#endif
_1myExceptions.h
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年8月13日17点38分
Last Version: V1.0
Descriptions: 综合各种异常
*/
#pragma once
#ifndef _MYEXCEPTIONS_H_
#define _MYEXCEPTIONS_H_
#include <string>
#include<iostream>
using namespace std;
// illegal parameter value
class illegalParameterValue
{
public:
illegalParameterValue(string theMessage = "Illegal parameter value")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// illegal input data
class illegalInputData
{
public:
illegalInputData(string theMessage = "Illegal data input")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// illegal index
class illegalIndex
{
public:
illegalIndex(string theMessage = "Illegal index")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// matrix index out of bounds
class matrixIndexOutOfBounds
{
public:
matrixIndexOutOfBounds
(string theMessage = "Matrix index out of bounds")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// matrix size mismatch
class matrixSizeMismatch
{
public:
matrixSizeMismatch(string theMessage =
"The size of the two matrics doesn't match")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// stack is empty
class stackEmpty
{
public:
stackEmpty(string theMessage =
"Invalid operation on empty stack")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// queue is empty
class queueEmpty
{
public:
queueEmpty(string theMessage =
"Invalid operation on empty queue")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// hash table is full
class hashTableFull
{
public:
hashTableFull(string theMessage =
"The hash table is full")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// edge weight undefined
class undefinedEdgeWeight
{
public:
undefinedEdgeWeight(string theMessage =
"No edge weights defined")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
// method undefined
class undefinedMethod
{
public:
undefinedMethod(string theMessage =
"This method is undefined")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
#endif
_2myFunctions.h
/*
Project name : allAlgorithmsTest
Last modified Date: 2022年8月13日17点38分
Last Version: V1.0
Descriptions: 综合各种非成员函数
*/
#pragma once
#ifndef _MYFUNCTIONS_H_
#define _MYFUNCTIONS_H_
#include<iostream>
#include "_1myExceptions.h"
#include<cmath>
#include <exception>
using std::min;
using std::endl;
using std::cout;
using std::bad_alloc;
/*交换两数据*/
template<class V>
void Swap(V& a, V& b)
{
V temp = a;
a = b;
b = temp;
}
/*
作用:将数组的长度加倍
输入:指针a指向需要改变长度的数组,oldLength表示数组原来的长度,newLength表示需要改变的新长度
结果:将数组扩容/缩容 为newLength
*/
template<class T>
void changeLength(T*& a, int oldLength, int newLength)
{
if (newLength < 0)
throw illegalParameterValue("new length must be >= 0");
T* temp = new T[newLength];
int number = min(oldLength, newLength);
copy(a, a + number, temp);
delete[] a;
a = temp;
}
/*遍历一维数组*/
template<class T>
void traverse1dArray(T* x, int length)
{
for (int i = 0; i < length; i++)
cout << x[i] << " ";
cout << endl;
}
/*创建二维数组*/
template <class T>
bool make2dArray(T**& x, int numberOfRows, int numberOfColumns)
{
try {
//行指针
x = new T * [numberOfRows];
//为每一行分配内存
for (int i = 0; i < numberOfRows; i++)
x[i] = new int[numberOfColumns];
return true;
}
catch (bad_alloc) { return false; }
}
/*遍历二维数组*/
template<class T>
void traverse2dArray(T**& x, int numberOfRows, int numberOfColumns)
{
for (int i = 0; i < numberOfRows; i++)
{
for (int j = 0; j < numberOfColumns; j++)
{
cout.width(4);
cout << x[i][j] << " ";
}
cout << endl;
}
}
#endif
运行结果
******************************binaryTreeChainsTest()函数开始**********************************
测试成员函数*******************************************
输入****************************
默认构造函数********************
Please enter the tree element:1
Please enter the tree element:2
Please enter the tree element:999
Please enter the tree element:999
Please enter the tree element:3
Please enter the tree element:999
Please enter the tree element:999
a.preOrderOutput() = 1 2 3
复制构造函数********************
b.preOrderOutput() = 1 2 3
前序中序构造函数****************
e.preOrderOutput() = 1 2 4 5 3 7 6
e.inOrderOutput() = 4 2 5 1 7 3 6
e.postOrderOutput() = 4 5 2 7 6 3 1
后序中序构造函数****************
f.preOrderOutput() = 1 2 4 5 3 7 6
f.inOrderOutput() = 4 2 5 1 7 3 6
f.postOrderOutput() = 4 5 2 7 6 3 1
输出****************************
前序输出************************
a.preOrderOutput() = 1 2 3
b.preOrderOutput() = 1 2 3
a.iterativePreOrder() = 1 2 3
中序输出************************
a.inOrderOutput() = 2 1 3
b.inOrderOutput() = 2 1 3
a.iterativeInOrder() = 2 1 3
后序输出************************
a.postOrderOutput() = 2 3 1
b.postOrderOutput() = 2 3 1
a.iterativePostOrder() = 2 3 1
层序输出************************
a.levelOrderOutput() = 1 2 3
b.levelOrderOutput() = 1 2 3
empty()*************************
a.empty() = 0
b.empty() = 0
size()**************************
a.size() = 3
b.size() = 3
compare()***********************
a.compare(b) = 1
Please enter the tree element:66
Please enter the tree element:88
Please enter the tree element:999
Please enter the tree element:999
Please enter the tree element:99
Please enter the tree element:999
Please enter the tree element:999
a.compare(c) = 0
swapTrees()*********************
a.preOrderOutput() = 1 2 3
a.preOrderOutput() = 1 3 2
height()************************
a.height() = 2
maxHeightDifference()***********
a.maxHeightDifference() = 0
layerMaxNumOfNode()*************
a.layerMaxNumOfNode() = 2
maxNumOfNodeInLayer()***********
a.maxNumOfNodeInLayer() = 2
******************************binaryTreeChainsTest()函数结束**********************************
*******************************treeExpressionTest()函数开始***********************************
二叉树表达式测试***************************************
前缀表达式构造函数**************
g.preOrderOutput() = / + - 1 2 + 3 4 * + 5 6 * 7 8
g.inOrderOutput() = 1 - 2 + 3 + 4 / 5 + 6 * 7 * 8
g.postOrderOutput() = 1 2 - 3 4 + + 5 6 + 7 8 * * /
中缀表达式构造函数**************
i.preOrderOutput() = / + - 1 2 + 3 4 * + 5 6 * 7 8
i.inOrderOutput() = 1 - 2 + 3 + 4 / 5 + 6 * 7 * 8
i.postOrderOutput() = 1 2 - 3 4 + + 5 6 + 7 8 * * /
后缀表达式构造函数**************
h.preOrderOutput() = / + - 1 2 + 3 4 * + 5 6 * 7 8
h.inOrderOutput() = 1 - 2 + 3 + 4 / 5 + 6 * 7 * 8
h.postOrderOutput() = 1 2 - 3 4 + + 5 6 + 7 8 * * /
计算树表达式的值****************
compulateTree()*****************
Please enter the tree element:+
Please enter the tree element:2
Please enter the tree element:#
Please enter the tree element:#
Please enter the tree element:3
Please enter the tree element:#
Please enter the tree element:#
d.preOrderOutput() = + 2 3
d.compulateTree() = 5
将后缀表达式转换为中缀表达式****
postExprToIn()******************
postExpr1[9] = 1 2 3 + 4 * 5 - +
inExpr1 = (1+(((2+3)*4)-5))
将前缀表达式转换为中缀表达式****
preExprToIn()*******************
preExpr1[9] = + 1 - * + 2 3 4 5
inExpr1 = (1+(((2+3)*4)-5))
将中缀表达式转换为后缀表达式****
inExprToPost()******************
inExpr[25] = ( ( - 1 ) + ( 2 + 3 ) ) / ( ( + 4 ) * ( 5 * 6 ) )
postExpr[13] = 1 - 2 3 + + 4 + 5 6 * * /
将中缀表达式转换为前缀表达式****
inExprToPre()******************
result = 16
result = 5
计算后缀表达式的值**************
postCalculate()*****************
result = 16
*******************************treeExpressionTest()函数结束***********************************
******************************treeApplicationTest()函数开始***********************************
二叉树应用测试*****************************************
设置信号放大器******************
Please enter the degradeToLeaf:0
Please enter the degradeFromParent:1
Please enter the degradeToLeaf:0
Please enter the degradeFromParent:2
0 0 1 0 0 3 1 3 1 0 0 1 1 1 3 0 0 1 0 1 2 0 0 1 0 1 1 1 3 3 0 3 0
0 3 0 1 1 3 1 3 1 0 0 1 0 0 3 0 0 1 1 3 3 0 1 2 0 0 1 0 1 1 0 0 1
0 0 1 1 3 1 0 0 3 1 1 3 0 0 1 0 3 0 0 0 1 0 1 2 1 3 3 0 0 1 0 1 1
并查集**************************
find(1) = 1 find(2) = 1
find(3) = 1 find(4) = 1
find(5) = 5 find(6) = 6
******************************treeApplicationTest()函数结束***********************************
Process finished with exit code 0
原文地址:https://blog.csdn.net/weixin_44410704/article/details/134632272
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_6639.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!