完全二叉树的权值
问题描述:
给定一颗包含N个结点的完全二叉树,每个结点都有一个权值,命名规律如图所示:
现在小明要把相同深度的结点权值加在一起,他想知道哪个深度的结点权值之和最大。如果有多个深度的权值和最大,请你输出其中最小的深度值。根的深度设为1。
输入和输出格式:
输入:
第一行包括一个整数N,表示有N个结点
第二行包含N个整数,代表 A 1 , A 2 , A 3 . . . A n A_{1},A_{2},A_{3}...A_{n} A1,A2,A3...An
输出:
一个整数,代表权值之和最大的深度值
分析:
因为在二叉树中结点权值排列的顺序和输入的顺序刚好是匹配的。
所以要对各个深度的权值进行求和,只需顺序遍历输入数据即可。
值得注意的是:完全二叉树第 n 层的结点个数是: 2 n − 1 2^{n-1} 2n−1
此题中,可以首先计算输入N得到的完全二叉树的深度,然后对每个深度进行计算。也可以不用计算深度,只需要计数当前求和了多少个元素,当为N个时,计算完就停止。
如果选择首先计算深度的话,因为二叉树的深度为: c e i l ( l o g 2 ( n + 1 ) ) ceil(log_{2}(n+1)) ceil(log2(n+1)),而C++中的库函数
log
求的是以e为底的。因而需要利用对数换底公式来计算:l o g a b = l o g c b l o g c a l o g 2 x = l n x l n 2 log_{a}b=\frac{log_{c}b}{log_{c}a}\\ log_{2}x=\frac{lnx}{ln2} logab=logcalogcblog2x=ln2lnx
其次,题目要求如果有多个深度的权值都是最大的,那么要输出最小的深度值。因为我们是从第1层开始计算的,所以只需设置第一层的权值和为最大值,只有当下一层的权值和大于当前max时,才更新结果,就可以满足题目要求。
还有一点,因为题目上说每个权值可正可负,所以在设置最初的最大权值和时,要么初始化一个特别小的数,要么就在计算第一层的和时,把权值和赋给最大权值和。这里采用设置一个大负数的方法 (0x3f3f3f3f)。
当我们需要设置一个int类型的最大值以用于某种功能时,首先我们能够想到使用: 0x7f ff ff ff
(4字节int类型的最大值),这个值为:2147483647
。
但是使用这个INF的时候,仅仅做一点运算便会造成溢出。因为为了给INF腾出一点运算的空间:
- 能够乘2倍不溢出
- 和
0x7f ff ff ff
一个数量级
所以使用:0x3f 3f 3f 3f
,也是一个十位数1061109567
,乘2也刚好不会溢出。
所以,可以利用 0x3f3f3f3f 来设置一个很大的量,作为界限值或者近似无穷来使用。
代码:
#include<iostream>
#include<cmath>
#define INF 0x3f3f3f3f
using namespace std;
int num[100007]; //存储输入数据
int main(void){
int N;
cin>>N;
for(int count=0;count<N;count++){
cin>>num[count];
}
int result=1, numIter=0, depSum=0, max=-INF;
//对每一层求和并进行比较
for(int depCount=1;depCount<=ceil(log(N+1)/log(2));depCount++){
depSum=0;
for(int temp=0;temp<pow(2,depCount-1);temp++){
depSum+=num[numIter++];
}
if(depSum>max){ //更新最大权值和
max=depSum;
result=depCount;
}
}
cout<<result;
return(0);
}
如果采取不计算深度的方式的话: