【秋招机试真题】华为机试0908-找出二叉树的最优节点

*    题目描述:
*        给出一颗二叉树,每个节点有一个编号和一个值,该值可能为负数,
*    请你找出一个最优节点(除根节点外),使得在该节点将树分成两棵树后
*    (原来的树移除这个节点及其子节点,新的树以该节点为根节点),分成
*    的两棵树各节点的和之间的差绝对值最大。请输出该节点编号,如有多个
*    相同的差,输出编号最小的节点。

*    输入描述:
*        4
*        4 9 -7 -8
*        0 1
*        0 3
*        1 2
*        第一行,四个节点,编号0-3,范围为1-10000
*        第二行,节点0-3的权值
*        第三行到第五行,表示二叉树各节点间的父子关系
*        0 1 // 节点0的左节点是1
*        0 2 // 节点0的右节点是2
*        1 2 // 节点1的左节点是2
*        注意:左节点永远出现在右节点之前
*            0:4
*            /  \
*         1: 9  3:-8
*          /      \
*         /
*       2:-7
*    输出描述:
*        节点编号,示例中编号为3的节点是最优节点
 


说明:

        华为0908第一题

        使用方法:不需要将二叉树构建起来,DFS计算每个节点作为root时的树节点的权值和,作为该节点新的权值


解题思路:

       其实不需要把树实现出来,只需要关注父节点和子节点的关系

        1、题目要求找出一个节点,该节点满足将该树分为两个子树,两个子树之间各节点的和的绝对值最大,假设当前节点为A,以节点A为root点的子树的和为x,很容易求出整树的和sum,那么要使abs(sum-x-(x))最大。所以要想办法求出每个节点以自身为root的子树的权重和来更新该节点的权重;

        2、重点问题是按照什么样的顺序更新权值,一定要从叶节点往上更新才是正确的,我一开始想的方法是通过统计节点的入度, 从入度为0的节点开始, 但是由于二叉树是多枝的,不容易实现。既然没办法找出叶节点,那就进行节点的遍历,然后对于每一个普通节点,进行递归更新权值,因为递归的本质就是栈,可以使叶节点的权值先更新。而且二叉树和递归就是天作之合!


代码如下:

#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
	// 使用DFS
void dfs(vector<vector<int>>& son, vector<int>& weight, vector<bool>& flag, int node_cur) {
	// 当前节点没有子节点时,不需要更新权值, 另外节点的权值已经更新,直接跳过
	if (son[node_cur].size() == 0 || flag[node_cur] == true) {
		flag[node_cur] = true;
		return;
	} 
	// 对于当前节点的权值更新,要先递归更新其当前节点的子节点权值,然后再更新当前节点权值
	for (int i = 0; i < son[node_cur].size(); ++i) {
		dfs(son, weight, flag, son[node_cur][i]);
		weight[node_cur] += weight[son[node_cur][i]];
	}
	//将已更新的权值的节点标记为true
	flag[node_cur] = true;
	return;
}
int main() {
	int n;
	cin >> n;
	// 记录每个节点的初始化权值
	vector<int> weight(n, 0);
	int sum = 0;
	for (int i = 0;i < n; ++i) {
		cin >> weight[i];
		//求所有节点权值和
		sum += weight[i];
	}
	int a, b;
	// 存储每个节点的子节点
	vector<vector<int>> son(n, vector<int>{});
	
	while (cin >> a >> b) {
		son[a].push_back(b);
	}
	// 记录该节点是否已经更新权值,如果是true,就代表权值更新了;
	vector<bool> flag(n, false);
	int max_value = 0;
	int res;
	for (int i = 0; i < n; ++i) {
		// 因为一定要从叶子节点向上更新权值才有效,但是确定叶子节点是较困难的,这种方法放弃
		// 所以采用遍历每个节点,对于一个普通节点,使用深度遍历递归的更新其子节点的权值
		// 这样保证权值的更新不会出现时间先后导致出错的问题
		// 然后对于每个已更新权重的节点标记为true,下次遇到的时候就直接过滤掉
		dfs(son, weight, flag, i);
		if (abs(sum - 2 * weight[i]) > max_value) {
			res = i;
			max_value = abs(sum - 2 * weight[i]);
		}
	}
	cout << res << endl;
	return 0;
}

执行结果如下:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值