《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题

二叉树和其他树

可编译运行程序见: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的左子树和右子树。
二叉树和树的根本区别是:

  • 二叉树的每个元素都恰好有两棵子树(其中一个或两个可能为空)。而树的每个元素可有任意数量的子树。
  • 在二叉树中,每个元素的子树都是有序的,也就是说,有左子树和右子树之分。而树的子树是无序的。

树和二叉树的另一个区别大概与定义有关,二叉树可以为空,但树不能为空。有的作者放宽了对树的定义,允许树为空。
和树一样,二叉树也是根节点在顶部。二叉树左(右)子树中的元素画在根的左(右)下方。每个元素节点和其子节点之间用一条边相连。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第1张图片

二叉树特性

二叉树的特性:

  • 特性 11-1 一棵二叉树有 n 个元素,n>0,它有 n-1 条边。

证明 二叉树的每个元素(除了根节点)有且只有一个父节点。在子节点与父节点间有且只有一条边,因此边数为n-1。

  • 特性 11-2 一棵二叉树的高度为h,h>0,它最少有h个元素,最多有 2 h 2^h 2h-1 个元素。

证明 因为每一级最少有1个元素,因此元素的个数最少为h。每个元素最多有2个子节点,则第i层节点元素最多为 2 h − 1 2^h-1 2h1 个,i>0。当 h=0 时,元素的总数为 0,也就是 2 0 − 1 2^0-1 201。当h>0 时,元素的总数不会超过 ∑ i = 1 h 2 h − 1 = 2 h − 1 \sum_{i=1}^h2^{h-1}=2^h-1 i=1h2h1=2h1

  • 特性 11-3 一棵二叉树有 n 个元素,n>0,它的高度最大为 n,最小高度为 ⌈ l o g 2 ( n − 1 ) ⌉ \lceil log_2(n-1)\rceil log2(n1)⌉

证明 因为每层至少有一个元素,因此高度不会超过n。由特性 11-2可以得知,高度为h 的二叉树最多有 2 h − 1 2^h-1 2h1个元素。因为 n< 2 h − 1 2^h-1 2h1,所以 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 \geq \lceil log_2(n-1)\rceil hlog2(n1)⌉

满二叉树

当高度为h的二叉树恰好有 2 h − 1 2^h-1 2h1个元素时,称其为满二叉树(full binary tree)。如图11-6所示。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第2张图片

完全二叉树

对高度为h的满二叉树的元素,从第一层到最后一层,在每一次中从左至右,顺序编号,从1到 2 h − 1 2^h-1 2h1(如图11-6所示)。假设从满二叉树中删除k个其编号为 2 h − i 2^h-i 2hi元素, 1 ≤ i ≤ k < 2 h 1≤i≤k<2^h 1ik<2h,所得到的二叉树被称为完全二叉树(complete binary tree)。满二叉树是完全二叉树的特例。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第3张图片

在完全二叉树中,一个元素与其孩子的编号有非常好的对应关系。

特性 11-4 设完全二叉树的一元素其编号为i, 1 < i < n 11<i<n。有以下关系成立:
1)如果 i = 1 i=1 i=1,则该元素为二叉树的根。若 i > 1 i>1 i>1,则其父节点的编号为 ⌊ i / 2 ⌋ \lfloor i/2\rfloor 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 是一棵树和其二叉树,其中实线表示指向左孩子的指针,虚线表示指向右孩子的指针。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第4张图片

二叉树的描述

数组描述

当缺少的元素数目多时,很浪费空间,因此不常用。

链表描述

二叉树最常用的表示方法是用指针。每个元素用一个节点表示,节点有两个指针域,分别称为leftChild和rightChild。除两个指针域之外,每个节点还有一个element域。

父节点的每一个指向孩子的指针表示一条边。因为 n个元素的二叉树仅有 n-1 条边,所以有2n-(n-1)=n+1个指针域没有值,它们被置为NULL。图 11-10 是二叉树的链表表示。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第5张图片

二叉树遍历

  • 前序遍历:根左孩子右孩子。对应于前缀表达式,在前缀(prefix)表达式中,操作符位于操作数之前,操作数从左到右顺序出现。不会存在歧义,无需括号和优先级。

  • 中序遍历:左孩子根右孩子。对应于中缀表达式,每个二元操作符(即有两个操作数的操作符)出现在左操作数之后,右操作数之前。但是这种表达式有歧义,如 x + y ∗ z x+y*z x+yz是解释为 ( x + y ) ∗ z (x+y)*z (x+y)z还是 x + ( y ∗ z ) x+(y*z) x+(yz)呢?因此有时候会使用括号来表示优先级。

  • 后序遍历:左孩子右孩子根。对应于后缀表达式,在后缀(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 ::compare(x),比较二叉树*this与二叉树x。当且仅当它们相同时,返回 true。测试你的代码。计算时间复杂性。
  • 46.编写方法linkedBinaryTree::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个单位。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第6张图片

设 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。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第7张图片

并查集

问题描述:有 n个元素从1 到 n编号。开始时,每一个元素在自己的类中,然后执行一系列find和combine操作。操作find(theElement)返回元素theElement所在类的唯一特征,而combine(a,b)把包含a和b的两个类合并。

建模为树:任何一个集合 S 都可以描述为一棵具有 ∣ S ∣ \lvert S \rvert S个节点的树,一个节点代表一个元素。任何一个元素可以作为根元素;剩余元素的任何子集可以作为根元素的孩子;再剩余元素的任何子素集可以作为根元素的孙子,等等。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第8张图片

举例:如图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++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第9张图片

C++实现:查集问题的求解策略是模拟指针的一个极好的应用。这里需要树的链表描述,其中每个节点必须有一个parent域,但不必有孩子域。还需要直接访问节点。为找到含有元素10的集合,先要找到元素10的节点,然后沿着节点的parent指针找到根节点。如果n个节点的索引号从1到n(n是元素个数),且节点e表示元素e,那么很容易实现直接访问。每个parent域给出父节点的索引,因此parent域为整数类型。
图11-18就是采用这种方法来表示图 11-16的。节点内的数字是其parent域的值,节点外的数字是该节点的索引。索引同时也是该节点所表示的元素。根节点的 parent 域被置为 0。因为没有索引是 0 的节点,所以 parent为0 表示不指向任何节点(即为空链)。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第10张图片

性能分析:构造函数的时间性能是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_2p\rfloor+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域不必修改。但是为简化起见,程序还是对路径上的每个节点都进行了修改)。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第11张图片

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第12张图片

路径紧缩代码

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第13张图片

在路径分割中,从e节点到根节点的路径上,除根节点和其子节点之外,每个节点的parent指针都被改为指向各自的祖父。在图11-20中,路径分割从节点13开始,结果得到图11-22 的树。在路径分割时,只考虑从e到根节点的一条路径就够了。
在路径对折中,从e节点到根节点的路径上,除根节点和其子节点之外,每隔一个节点,其parent指针都被改为指向各自的祖父。在路径对折中,指针改变的个数仅为路径分割中的一半。同样,在路径对折中只考虑从e到根的一条路径就够了。在图11-20中,路径对折从节点13开始,结果得到图11-23的树。

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题_第14张图片

仿真测试

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::visit)(binaryTreeNode*) = 0;
	2.不能单独专门化成员模板函数,只能针对整个类专门化。
	3.在模板函数中可以使用typeid()区别对待特定数据类型。
本程序注意事项:
	1.所有关于前缀、后缀、中缀表达式的全部使用了char类型代表元素,char类型数组存储整个表达式
*/
#pragma once
#ifndef _BINARYTREECHAINS_H_
#define _BINARYTREECHAINS_H_
#include 
#include 
#include 
#include 
#include 
#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*
    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(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::visit)(binaryTreeNode*) = 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,'\0' });
        else
        {
            /*每执行一次计算就添加括号,每个数据都加括号实在不需要,看起来很冗余*/
            b = st.top() + '\0';
            st.pop();
            a = st.top() + '\0';
            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,'\0' });
        else
        {
            /*每执行一次计算就添加括号,每个数据都加括号实在不需要,看起来很冗余*/
            a = st.top() + '\0';
            st.pop();
            b = st.top() + '\0';
            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 
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 
#include

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
#include "_1myExceptions.h"
#include
#include 
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

你可能感兴趣的:(数据结构,算法与应用,C++语言描述学习笔记,数据结构,算法,c++)