完全二叉树的权值
【问题描述】
给定一棵包含 N 个节点的完全二叉树,树上每个节点都有一个权值,按从
上到下、从左到右的顺序依次是 A1, A2, · · · AN,如下图所示:
现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点
权值之和最大?如果有多个深度的权值和同为最大,请你输出其中最小的深度。
注:根的深度是 1。
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数 A1, A2, · · · AN 。
【输出格式】
输出一个整数代表答案。
【样例输入】
7
1 6 5 4 3 2 1
【样例输出】
2
【评测用例规模与约定】
对于所有评测用例,1 ≤ N ≤ 100000, 100000 ≤ Ai ≤ 100000。
第一种解法的思路:
可以不需要构建二叉树的结构,直接利用完全二叉树的深度和节点个数的特性来做,只需要准备一个临时数组,存储二叉树每一层的值并在循环过程中替换最大值,并将修改最大深度。
完全二叉树的特性:每层(除最后一层)的节点数 = 2的n次方.(深度从0开始)
即Math.pow(2,i)的值也就是内层循环的循环次数,用来求该层的权值和。
二叉树中每层第一个节点的下标关系:j = Math.pow(2,i)-1; i是深度(从0开始)
代码如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args){
int n;
Scanner s = new Scanner(System.in);
n = s.nextInt();
if (n == 1){
System.out.println(1);
return;
}
int[] a = new int[n];
for(int i=0;i<n;i++){
a[i] = s.nextInt();
}
int sum[] = new int[n];;
int index = 0;
int deep = 1;
int max = a[0];
for(int i=0;i<n/2;i++){
//二叉树中每层第一个节点的下标关系:j = Math.pow(2,i)-1; i是深度(从0开始)
for(int j = (int) Math.pow(2,i)-1, k = 0; k<Math.pow(2,i) && j<n ; k++,j++){
sum[index] += a[j];
}
//更新当前的最大值
if (sum[index] > max){
max = sum[index];
//深度是从1开始,需要+1
deep = index + 1;
}
index++; //准备计算下一层的权值和
}
System.out.println(deep);
}
}
第一种解法在蓝桥杯官网的提交结果
第二种解法的思路:
可以自己构建一个二叉树的结构,然后利用二叉树的层次遍历方法求每一层的权值和并进行比较,记录最大的深度。
这种解法的关键在于按每层来构建二叉树和根据当前层的节点数求当前的深度
二叉树中节点数和深度的关系:当前深度等于log以2为底,当前的节点个数(size)为真数求出的结果取整再+1。即depth = (int)log2(size) + 1;
但java没有直接以2为底求的log值方法,所以需要利用log的换底公式:
代码如下:
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Map;
import java.util.Scanner;
/**
* 完全二叉树的权值
*/
public class Main {
static int n = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int arr[] = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
//第一个值为根节点
Tree root = new Tree(arr[0]);
//创建树
createTree(arr, root);
int max = cal(root);
System.out.println(max);
}
//计算最终结果
private static int cal(Tree root) {
ArrayDeque<Tree> queue = new ArrayDeque<>();
//先将根节点入队
queue.add(root);
int depth;
int maxDepth = 1;
int max = root.value;
int sum; //计算每层的权值和
while (!queue.isEmpty()){
//每次循环结束,都需要将sum置为0
sum = 0;
int size = queue.size(); //当前队列中的元素个数就是每层的节点数,也就是要循环的次数
//***求当前的深度
//二叉树中深度与节点数的关系:depth = (int)log2(size) + 1;
//因为计算机里面没有log以2为底的对数,所以我们可以利用log的换底公式
depth = (int) (Math.log(size)/Math.log(2)) + 1;
for (int i = 0; i < size; i++) {
Tree node = queue.poll();
if (node.left != null){
sum += node.left.value;
queue.add(node.left);
}
if (node.right != null){
sum += node.right.value;
queue.add(node.right);
}
}
if (sum > max) {
max = sum;
maxDepth = depth;
}
}
return maxDepth+1; //注意最后+1,题目说的深度从1开始
}
//利用队列来构建二叉树
public static void createTree(int[] values, Tree node) {
ArrayDeque<Tree> queue = new ArrayDeque<>();
queue.add(node);
int depth = 0;
while (!queue.isEmpty() && (2 * depth + 2 <= values.length)) {
Tree temp = queue.poll();
if (temp.left == null) {
temp.left = new Tree(values[2 * depth + 1]);
queue.add(temp.left);
}
if (temp.right == null && 2 * depth + 2 < values.length) {
temp.right = new Tree(values[2 * depth + 2]);
queue.add(temp.right);
}
depth++;
}
}
}
//二叉树的节点
class Tree {
int value;
Tree left;
Tree right;
public Tree(int value) {
this.value = value;
}
}
第二种解法在蓝桥杯官网的提交结果
总结:以上两种方法虽然都能全部通过问题案例,但是第一种方法实现起来更简单,代码量更少,也更容易想到,不过第二种解法可以作为加强学习二叉树的数据结构来写。