c语言-数据结构-如何用分冶法求得二叉树的节点数与高度?
二叉树
- 前言
- 一、二叉树的高度
- 二、二叉树的节点总数
- 三、二叉树的第k层节点的个数
前言
本文讲解如何求出二叉树的高度等问题
一、二叉树的高度
我们有两种方式,一种是遍历,第二种则是分冶法,因为遍历比较麻烦并且容易出错,遗漏节点,因此我们在这里讲解优解,也就是分冶法
顾名思义,分冶法,便是将一个问题转化为若干个小问题去解决,而我们遍历二叉树,二叉树分为根,左子树和右子树,那么我们便可以遍历根,将遍历左子树和右子树分别视为两个小问题
比如
以这张图为例,校长想要知道学校里的职务共分了多少级别,但他不能一个个的去找人调查,那么他就调用他所知道的两个院长,让院长分别去统计
院长呢,就调用自己手下的系主任,让系主任分别去调查
系主任就调用自己手下管理的辅导员,辅导员接着调用班长,班长调用舍长,就这么一层一层的调用下去,最后再一层层地返回,就可以得到最终结果
而最终分了多少级别,看的是级别最多的一方加本身这个级别,比如说信息学院中,院长设置了一个部长级别,部长下一个级别是系主任级别,而数理学院却没有设置部长级别,那么信息学院就比数理学院多了一个级别,并且级别数量要按照信息学院的来算,院长得到学院里除了自己之外的级别数量,再加上自己本身,便得到了整个学院的级别总数
同样,计算二叉树的高度也是如此,我们将根节点的左子树高度和右子树高度进行比较,得到高度大的那一方,加上根节点本身的一层,便是二叉树的高度
即:二叉树的高度是左右子树高度大的 + 1
如图,lenum代表左子树遍历,rinum代表右子树遍历
我们从A节点开始,判断不为空,走下一步;
下一步执行TreeHeight(root->left),走到了B节点;
判断B节点不为空,下一步走到了D节点;
判断D节点不为空,下一步传递D节点的left指针走到了NULL,判断为空,返回到传D节点的函数,此时D的lenum为0;执行下一步,传递D节点的right指针,走到了NULL,判断为空,返回到传D节点的函数,此时D的rinum为0,进行rinum和lenum的大小比较,比较之后将大的那个数加1
注:加1的原因是它本身也要算是一个节点,比如一棵只有ABC三个节点的树,A的lenum和rinum都是1,比较之后采用rinum(左右子树高度相等时代码采取右子树的高度,也可修改为左子树的高度),而A节点本身也是独占一层,那么就需要rinum + 1,也就是算上本身
此时返回到传B节点的函数中,左子树D返回的值是1,即B的lenum为1
而右子树E同理,返回也是1,那么B的rinum也为1
比较后相等,则采用rinum + 1,此时传递B节点的函数返回2
也就是说A的lenum为2,接着进入A的右子树CFG,返回也为2,即rinum为2
之后进行判断,相等,采取rinum + 1 = 3,返回3,结束
最终得到该二叉树的高度为3
有的写法可能更加简洁,但是过程却不一定,比如:
int TreeHeight(Tree* root)
{if (root == NULL){return 0;}
return TreeHeight(root->left) > TreeHeight(root->right)? TreeHeight(root->left) + 1: TreeHeight(root->right) + 1;
}
这种写法代码看似是简便了,但是我们我们每个节点都会被传递调用TreeHeight函数很多次
比如,我们在比较TreeHeight(root->left) > TreeHeight(root->right)时,就要从A节点到NULL全部进行一次遍历,判断完成之后,我们要返回数值对吧,但是返回的是TreeHeight(root->left) + 1或者是 TreeHeight(root->right) + 1,我们不知道这两个函数的返回值是多少,因为我们刚刚只是用数值进行了比较,并没有定义一个变量开辟空间去存储它们的值,因此返回之后这个值就消失了,相当于没有任何一个院长、系主任记得这个数,校长也不记得,所以我们还要再次调用这个函数去求他具体的数值是多少
而图中处于最底层的舍长,先调用左NULL,再进入右NULL,返回,后面求值再调用左NULL,再调用右NULL,需要调用很多次,相当于舍长要一次次的统计宿舍人数,这就会使得过程十分麻烦,因此我们并不建议使用这种方法,而是采用每次都定义变量开辟空间去存储它的值
二、二叉树的节点总数
同理,求二叉树的节点总数 ,相当于校长想要知道学校的总人数
比如,当刚开学时,学校人数较少的时候,比如十几个人,校长如果一个一个统计的话会很方便,但是当学校正式开学学生全部到校有几千人时,校长不可能拿着小本本挨个人统计,因为这会很麻烦,此时校长就可以调用手下的院长,让院长统计每个院的人数,然后相加再加自己本身就得到学校的总人数了
而每个院长就会调用自己手下的系主任,让系主任去统计每个系有多少人,依次相加再加本身,就得到自己院的人数了
因此,统计二叉树节点总数时,我们要采用分冶法去解决
如图,我们同样采用分冶法,结果就是左子树所有节点+右子树所有节点+本身(1)
我们采用左子树和右子树相加加本身的解法,恰好可以计算到所有节点,比如我们通过计算演示得到D节点的TreeSize(root->left) 和TreeSize(root->right)均为0,返回0 + 0 + 1(1为D节点本身),此时回到传递B节点的函数中,此时B节点的TreeSize(root->left)结束,并且值为1,进入TreeSize(root->right),计算出也是1,那么TreeSize(root->left) + TreeSize(root->right)为2,最后加本身,即加1,得到3
验证BDE刚好为三个节点,后面过程同上
三、二叉树的第k层节点的个数
如图,我们想要求二叉树中第k层节点的个数,依旧可以从左右子树方面入手,因为某一层的节点个数便是由左子树节点数加右子树节点数得到
如图,我们将A节点定义为第一层,将DEFG定义为第k层,那么当我们针对A节点的左子树BDE时
此时,B节点为根节点,我们可以将B视为第一层,那么DEFG便是第k-1层
由此,我们可以得到:二叉树的第k层节点个数便是左右子树的第k-1层节点个数之和
如图代码所示,当root指向NULL时,返回0,也就是0个节点
当k = 1时,代表我们到达了第k层,并且此时root并不指向NULL,因此返回1,代表此处有一个节点
注:k == 1的判断条件是在root == NULL的判断语句之后,因此无需担心野指针风险
为什么是k = 1的时候到达第k层呢?
我们根据完整的二叉树图片来看,A节点在第一层,DEFG在第K层,那么我们可以倒过来看,将A节点视为第k层,DEFG视为第一层,那么k到1与1到k都是相同的路程,因此k=1时便代表到达了第k层
最后我们一步步地返回,执行左右子树的TreeKSize函数,将其相加即可得到第k层的节点个数