《数据结构、算法与应用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的左子树和右子树。
二叉树和树的根本区别是:

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

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

在这里插入图片描述

二叉树特性

二叉树的特性:

  • 特性 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所示。

在这里插入图片描述

完全二叉树

对高度为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)。满二叉树是完全二叉树的特例。

在这里插入图片描述

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

特性 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/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 是一棵树和其二叉树,其中实线表示指向左孩子的指针,虚线表示指向右孩子的指针。

在这里插入图片描述

二叉树的描述

数组描述

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

链表描述

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

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

在这里插入图片描述

二叉树遍历

  • 前序遍历:根左孩子右孩子。对应于前缀表达式,在前缀(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 <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 ∣ \lvert S \rvert 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_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域不必修改。但是为简化起见,程序还是对路径上的每个节点都进行了修改)。

在这里插入图片描述

在这里插入图片描述

路径紧缩代码

在这里插入图片描述

在路径分割中,从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);elseroot = 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 treeroot = new binaryTreeNode<E>(element, left.root, right.root);treeSize = left.treeSize + right.treeSize + 1;// deny access from trees left and rightleft.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;elsereturn ++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;elsereturn 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;elsethrow illegalParameterValue("除数不能为0!");}
}/*使用全部是二元操作符的前缀表达式创建二叉树*/
/*从尾元素开始遍历表达式的元素*/
/*如果是数据,则生成binaryTreeNode并入栈*/
/*如果不是数据,则生成binaryTreeNode,从栈中弹出两个数据形成其子树,第一个弹出的是其左子树,第二个弹出的是其右子树;然后再将当前结点入栈*/
template<class E>
binaryTreeNode<E>* binaryTreeChains<E>::preExprCreateTree(E expression[],int length)
{stack<binaryTreeNode<E>*> st;//用于存储已经处理的数据生成的binaryTreeNodebinaryTreeNode<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;//用于存储已经处理的数据生成的binaryTreeNodestack<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);elseswitch (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;//用于存储已经处理的数据生成的binaryTreeNodebinaryTreeNode<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和1cin >> b;// 这里输入0和2binaryTreeChains<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;//右括号的优先级是0default: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();elseinExpr = "后缀表达式非法,转换失败!";
}
/*练习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);elseswitch (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);elseswitch (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();elseinExpr = "后缀表达式非法,转换失败!";
}/*计算表达式的值*/
/*前缀表达式计算表达式的值*/
/*参数说明: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);elsethrow illegalParameterValue("除数不能为0!");break;default:break;}}}if (st.size() == 1)return st.top();elsecout << "前缀表达式非法,计算失败!" << 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!");elsereturn 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);elseswitch (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);elsethrow illegalParameterValue("除数不能为0!");break;default:break;}}}if (st.size() == 1)return st.top();elsecout << "后缀表达式非法,计算失败!" << 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 levelreturn 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 treebool root;   // true iff roottreeUnionFindNode(){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 3empty()*************************
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/171992.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

04_MySQL备份与恢复

任务背景 一、真实案例 某天&#xff0c;公司领导安排刚入职不久的小冯同学将生产环境中的数据(MySQL数据库)全部导入到测试环境给测试人员使用。当小冯去拿备份数据时发现&#xff0c;备份数据是1个礼拜之前的。原因是之前运维同事通过脚本每天对数据库进行备份&#xff0c;…

51单片机蜂鸣器发出悦耳的声音

51单片机蜂鸣器发出悦耳的声音 1.概述 这篇文章介绍单片机控制蜂鸣器入门小实验&#xff0c;通过该实验掌握蜂鸣器发声的原理&#xff0c;控制声音发出我们想听的音乐。 2.蜂鸣器发声 2.1.硬件原理 1.蜂鸣器正极接单片机20号引脚VCC&#xff0c;负极接19号引脚P1.7 2.20MH…

【数据处理】 -- 【两分钟】了解【最好】的方式 -- 【正则表达式】

直接匹配&#xff1b; 普通字符 元匹配&#xff1a; . 任意单字符 r’表示单引号里字符为其特殊含义&#xff0c;比如.不是句号是匹配符的意思 *任意次数&#xff08;换行结束&#xff09; 一次及以上 {3,4}指定次数,至少3次&#xff0c;最多4次|{3}固定4次 [\d.]单个任意…

Kotlin学习——kt里的作用域函数scope function,let,run,with,apply,also

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…

什么是分布式锁?Redis实现分布式锁详解

目录 前言&#xff1a; 分布式系统买票示例 引入redis做分布式锁 引入过期时间 引入校验id 引入lua脚本 过期时间续约问题 redlock算法 小结&#xff1a; 前言&#xff1a; 在分布式系统中&#xff0c;涉及多个主机访问同一块资源&#xff0c;此时就需要锁来做互斥控制…

【Java】线程池的简单实用

1、什么是线程池 Java当中&#xff0c;为了规避频繁创建调度进程的开销&#xff0c;我们引入了线程。但是如果进一步提高创建销毁频率&#xff0c;线程的开销也不容忽视。 对此我们有两个解决方案 协程&#xff08;轻量级线程&#xff09;&#xff1a;相比线程&#xff0c;把…

大数据平台/大数据技术与原理-实验报告--部署全分布模式Hadoop集群

实验名称 部署全分布模式Hadoop集群 实验性质 &#xff08;必修、选修&#xff09; 必修 实验类型&#xff08;验证、设计、创新、综合&#xff09; 综合 实验课时 2 实验日期 2023.10.16-2023.10.20 实验仪器设备以及实验软硬件要求 专业实验室&#xff08;配有cen…

6.4 Windows驱动开发:内核枚举DpcTimer定时器

在操作系统内核中&#xff0c;DPC&#xff08;Deferred Procedure Call&#xff09;是一种延迟执行的过程调用机制&#xff0c;用于在中断服务例程&#xff08;ISR&#xff09;的上下文之外执行一些工作。DPC定时器是基于DPC机制的一种定时执行任务的方式。 DPC定时器的主要特…

Namecheap怎么样,Namecheap优惠码以及注册手把手教程

Namecheap 是一家成熟的服务器域名托管公司&#xff0c;可以为合适的客户提供良好的解决方案。这些优点和缺点应该让您清楚地了解您的期望&#xff0c;以便您知道这是否是您网站的正确选择。 Namecheap怎么样&#xff1f; 已成立的公司&#xff1a; Namecheap 已经营 20 多年…

【代码随想录刷题】Day18 二叉树05------延伸题目练习

文章目录 1.【113】路径总和II1.1 题目描述1.2 解题思路1.3 java代码实现 2.【105】从前序与中序遍历序列构造二叉树2.1 题目描述2.2 java代码实现 【113】路径总和II 【105】从前序与中序遍历序列构造二叉树 1.【113】路径总和II 1.1 题目描述 给你二叉树的根节点 root 和一…

Vscode工具使用指南

通用 快捷键文件 / 编辑查找 / 替换窗口插件主题 连接linux 快捷键 文件 / 编辑 新建文件&#xff1a;CtrlN放大或缩小&#xff1a;Ctrl /-代码行缩进&#xff0c;展开&#xff1a;Ctrl[ 和 Ctrl]在当前行下方插入一行&#xff1a;CtrlEnter在当前行上方插入一行&#xff1a;…

EMQX-5.3.1单机集群部署并基于Nginx实现负载均衡

本例单机集群部署使用三个节点&#xff0c;分别为node1、node2、node3 一、安装与配置 1 创建数据目录 mkdir -p node1/data node1/logs mkdir -p node2/data node2/logs mkdir -p mode3/data node3/logs 2 数据目录授权 chown 1000 node1/ node2/ node3/ chown 1000 n…

jsp生成验证码的代码

效果图&#xff1a; loginProcess.jsp <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%><% String captcharequest.getParameter("captcha");%><% String captcha_session(String)s…

MySQL基本SQL语句(上)

MySQL基本SQL语句&#xff08;上&#xff09; 一、客户端工具的使用 1、客户端工具mysql使用 mysql: mysql命令行工具&#xff0c;一般用来连接访问mysql数据库 选项说明-u, --username指定登录用户名-p, --password指定登录密码(注意是小写p),一定要放到最后面-h, --hostn…

HDFS JAVA API的应用

首先把hadoop服务起来 1. (简答题) 使用HDFS 的JAVA API 进行编程&#xff1a; &#xff08;1&#xff09;获取自己HDFS集群下的所有文件和目录&#xff1b; //获取自己HDFS集群下的所有文件和目录&#xff1b;import org.apache.hadoop.conf.Configuration; import org.apa…

究竟FactoryBean是什么?深入理解Spring的工厂神器

文章目录 前言什么是FactoryBean&#xff1f;如何使用FactoryBean&#xff1f;我们常见的FactoryBeanBeanFactory 和 FactoryBean&#xff1f;FactoryBean后续&#xff1f;MapperFactoryBean 前言 在Spring框架中&#xff0c;bean的创建通常交由Spring IoC容器负责&#xff0c…

【从亮机卡开始的云炼丹】环境配置记录debug

要更改Anaconda环境的默认路径到D盘 可以按照以下步骤操作&#xff1a; 1. 打开Anaconda Prompt&#xff08;或者命令行窗口&#xff09;。 2. 输入以下命令更改Anaconda环境的默认路径到D盘&#xff1a; conda config --set envs_dirs D:\Anaconda\envs 这将把Anaconda环境…

汽车租聘管理与推荐系统Python+Django网页界面+协同过滤推荐算法

一、介绍 汽车租聘管理与推荐系统。本系统使用Python作为主要编程语言&#xff0c;前端采用HTML、CSS、BootStrap等技术搭建前端界面&#xff0c;后端采用Django框架处理用户的请求。创新点&#xff1a;使用协同过滤推荐算法实现对当前用户个性化推荐。 其主要功能如下&#x…

设计模式—依赖倒置原则(DIP)

1.概念 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff09;是程序要依赖于抽象接口&#xff0c;不要依赖于具体实现。简单的说就是要求对抽象进行编程&#xff0c;不要对实现进行编程&#xff0c;这样就降低了客户与实现模块间的耦合。 通俗的讲&#xff1…

SpringBoot校验List失效解决方法

文章目录 SpringBoot校验List失效解决方法附&#xff1a;校验基本数据类型和String类型的方法参数时也需要在类上加Validated SpringBoot校验List失效解决方法 失效场景示例代码&#xff1a; RestController RequestMapping("/v1/jx/flowSummary") Slf4j public cl…