【面试编程题】求树的最大求和路径

题目描述

给定一个二叉树,找出最大的求和路径。可以从任意一点出发,沿着树的父子关系到树上的任意一点,路径可以以任意节点作为起点和终点。
例如
在这里插入图片描述

返回最长求和路径为2-1-3,以及最大路径和6

求最大路径和的思路

拿到这题目的第一时间我想到的就是递归求值(事实证明这个方法确实可行),即首先从树的最左边开始计算路径和,上面的图太层数太低不方便描述,以下图为例子:在这里插入图片描述
正式描述前先说一下第一个也是最大一个坑:结点的路径值可能为负…这就导致了不能使用

dp父=max(dp左孩子+左孩子的值,dp右孩子+右孩子的值)

这样一个dp方程式来算出结果。
相信很多人会问为什么呢?
答案很简单,因为一个结点的最大路径与它所传递给父节点的最大路径并不一样,以2号节点为例,它所能达到的最大路径是这样:在这里插入图片描述
2+4+5返回路径和为11,但它作为子节点返回给1的结果只能是在这里插入图片描述
2+5也就是7。
但这样的也方便解决,即定义一个sum值存放当前结点所能达到的值,然后方法返回的值为能传递的最大值,如下

Math.max(left, right) + node.value//能传递的最大值

一次遍历完成后进行这样一个算式

sum = Math.max(sum, node.value + left + right)//能达到的最大值

遍历的关键部分解决,下面解决可能为负数的情况,这个的解决方法就更简单了,首先这样一个问题,一个数一定会返回给你正数或负数,那么你为了达到最大值,这个负数的你要不要?当然不要!所以遇到底层结点返回给你负数的情况你就将这个返回值与0进行比较,比0小不要,以0替换,表示不走这个路径。实现方程下:

int left = Math.max(0, getMax(node.left));// 递归计算左子树的最大路径和,如果最大路径和为负数,返回0
int right = Math.max(0, getMax(node.right));// 递归计算右树的最大路径和,如果最大路径和为负数,返回0

好了这就是主要解决过程。下面贴上代码以及一个代码执行过程的讲解。

代码

private int sum = Integer.MIN_VALUE;// 最终返回的数据值,因为你不知道结点是不是全负数,所以定义一个最小的数

	public int MaxSumPath(TreeNode node) {// 对树的每个结点递归调用获取最大值的函数,最后将返回根结点的最大路径和
		getMax(node);
		getPath(node, sum);
		System.out.println(getPath(node, sum));
		return sum;
	}

	private int getMax(TreeNode node) {// 单个节点的最大路径求法,运用的主要方法是递归,主要思路在于获取左子树的最大路径和和右子树的最大路径和,如果其中哪个子树的最大路径和为负数则返回0,即不考虑这一子树。
		if (node == null) {// 没有节点,返回空值
			return 0;
		}
		int left = Math.max(0, getMax(node.left));// 递归计算左子树的最大路径和,如果最大路径和为负数,返回0
		int right = Math.max(0, getMax(node.right));// 递归计算右树的最大路径和,如果最大路径和为负数,返回0
		sum = Math.max(sum, node.value + left + right);// 最大路径和。(当前所能达到的最大路径和)
		return Math.max(left, right) + node.value;// 返回最大路径(即当前结点能给父节点的最大路径和)
	}

运行过程以以下图为例:
在这里插入图片描述
首先程序会因为这条程序语句

int left = Math.max(0, getMax(node.left));

到树的最左叶子结点,即4号节点,此时sum(结点所能达到的最大值)=4,返回给2号节点的值也为4。
然后回溯到了2,又因为这条语句

int right = Math.max(0, getMax(node.right));

到了节点5,此时的sum=4<5,所以sum被5替换,同时结点5返回给2号节点的值也为5。
然后2号结点执行

sum = Math.max(sum, node.value + left + right);
return Math.max(left, right) + node.value;

这两条语句,sum=5<4+5+2=11,故sum变成11,2号节点返回给1号节点的值为2+5=7。
然后1号节点执行

int right = Math.max(0, getMax(node.right));

跟上述步骤一样,到了右子树的最左子节点,也就是6号节点,执行程序,sum=11>6,不替换。返回给3号节点6。
然后因为3号节点的

int right = Math.max(0, getMax(node.right));

到了7号节点,执行程序,sum=11>7,不替换。返回给3号节点7。
然后3号执行

sum = Math.max(sum, node.value + left + right);
return Math.max(left, right) + node.value;

sum=11<6+7+3=16,sum变成16,同时返回给1号节点7+3=10.
然后1号节点执行最后两步骤:

sum = Math.max(sum, node.value + left + right);
return Math.max(left, right) + node.value;

此时sum=11<10+7+1=18,发生替换,sum=18,然后它所能给父节点(如果有的话)为10+1=11。
程序执行完成,将sum输出。

求最大路径

自我关于问题的疑问

实话说这个题目的最大路径我并没有完整解出来,如果有人看到此处并解出来的麻烦在评论区评论告诉一下我思路,我想到的方法是一个最蠢的方法,即我现在测试用的树是一个高度为3的树,那么我手动算出所有结点走的路径的所有可能以及所能达到的路径和,然后将这些路径和与我上面求得的最大路径和进行比较,输出路径。这样解是解出来了,问题也来了,3阶你能给出所有结果,4阶你能吗?5阶、6阶呢?所以我还是希望有人能教一下我怎么解…以下是代码:
也许有人觉得我上述代码能算出最大路径那么为什么给不出最大路径值呢?
问题就在于一个结点不仅有最大路径和,还有一个所能传递给上层结点的路径和,什么时候使用最大路径和,什么时候使用所能传递给上层结点的路径和这变成了最大的问题。并且以1号节点为例,它所接受到的是7和11,我总不能直接输出7-1-11吧…因为根本没这两个结点…这就是我所解不出来的疑点所在…

private String getPath(TreeNode node, int sum) {// 对二叉树的所有可能运行存在的路径和进行求解,输出更最大路径和相同的路径
		String path = null;
		if (sum == node.left.left.value) {
			path = String.valueOf(node.left.left.value);
		} else if (sum == node.left.right.value) {
			path = String.valueOf(node.left.right.value);
		} else if (sum == node.right.left.value) {
			path = String.valueOf(node.right.left.value);
		} else if (sum == node.right.right.value) {
			path = String.valueOf(node.right.right.value);
		} else if (sum == node.left.value) {
			path = String.valueOf(node.left.value);
		} else if (sum == node.right.value) {
			path = String.valueOf(node.right.value);
		} else if (sum == node.value) {
			path = String.valueOf(node.value);
		} else if (sum == node.left.left.value + node.left.value) {
			path = String.valueOf(node.left.left.value) + "-" + String.valueOf(node.left.value);
		} else if (sum == node.left.right.value + node.left.value) {
			path = String.valueOf(node.left.right.value) + "-" + String.valueOf(node.left.value);
		} else if (sum == node.right.left.value + node.right.value) {
			path = String.valueOf(node.right.left.value) + "-" + String.valueOf(node.right.value);
		} else if (sum == node.right.right.value + node.right.value) {
			path = String.valueOf(node.right.right.value) + "-" + String.valueOf(node.right.value);
		} else if (sum == node.value + node.left.value) {
			path = String.valueOf(node.left.value) + "-" + String.valueOf(node.value);
		} else if (sum == node.value + node.right.value) {
			path = String.valueOf(node.value) + "-" + String.valueOf(node.right.value);
		} else if (sum == node.left.value + node.left.left.value + node.left.right.value) {
			path = String.valueOf(node.left.left.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.left.right.value);
		} else if (sum == node.right.value + node.right.left.value + node.right.right.value) {
			path = String.valueOf(node.right.left.value) + "-" + String.valueOf(node.right.value) + "-"
					+ String.valueOf(node.right.right.value);
		} else if (sum == node.value + node.left.value + node.left.left.value) {
			path = String.valueOf(node.left.left.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value);
		} else if (sum == node.value + node.left.value + node.left.right.value) {
			path = String.valueOf(node.left.right.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value);
		} else if (sum == node.value + node.right.value + node.right.left.value) {
			path = String.valueOf(node.right.left.value) + "-" + String.valueOf(node.right.value) + "-"
					+ String.valueOf(node.value);
		} else if (sum == node.value + node.right.value + node.right.right.value) {
			path = String.valueOf(node.right.right.value) + "-" + String.valueOf(node.right.value) + "-"
					+ String.valueOf(node.value);
		} else if (sum == node.value + node.right.value + node.left.value) {
			path = String.valueOf(node.left.value) + "-" + String.valueOf(node.value) + "-"
					+ String.valueOf(node.right.value);
		} else if (sum == node.value + node.left.value + node.left.left.value + node.right.value) {
			path = String.valueOf(node.left.left.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value) + "-" + String.valueOf(node.right.value);
		} else if (sum == node.value + node.left.value + node.left.right.value + node.right.value) {
			path = String.valueOf(node.left.right.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value) + "-" + String.valueOf(node.right.value);
		} else if (sum == node.value + node.left.value + node.right.left.value + node.right.value) {
			path = String.valueOf(node.left.value) + "-" + String.valueOf(node.value) + "-"
					+ String.valueOf(node.right.value) + "-" + String.valueOf(node.right.left.value);
		} else if (sum == node.value + node.left.value + node.right.right.value + node.right.value) {
			path = String.valueOf(node.left.value) + "-" + String.valueOf(node.value) + "-"
					+ String.valueOf(node.right.value) + "-" + String.valueOf(node.right.right.value);
		} else if (sum == node.left.left.value + node.left.value + node.value + node.right.value
				+ node.right.left.value) {
			path = String.valueOf(node.left.left.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value) + "-" + String.valueOf(node.right.value) + "-"
					+ String.valueOf(node.right.left.value);
		} else if (sum == node.left.right.value + node.left.value + node.value + node.right.value
				+ node.right.left.value) {
			path = String.valueOf(node.left.right.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value) + "-" + String.valueOf(node.right.value) + "-"
					+ String.valueOf(node.right.left.value);
		} else if (sum == node.left.left.value + node.left.value + node.value + node.right.value
				+ node.right.right.value) {
			path = String.valueOf(node.left.left.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value) + "-" + String.valueOf(node.right.value) + "-"
					+ String.valueOf(node.right.right.value);
		} else if (sum == node.left.right.value + node.left.value + node.value + node.right.value
				+ node.right.right.value) {
			path = String.valueOf(node.left.right.value) + "-" + String.valueOf(node.left.value) + "-"
					+ String.valueOf(node.value) + "-" + String.valueOf(node.right.value) + "-"
					+ String.valueOf(node.right.right.value);
		}
		return path;
	}

题外话

其实在机试时面试老师给了个提示(虽然我个人认为这个提示好像是错误的),下面我来描述一下:
首先从最左子树的父节点开始程序,以以下的图为例子:
在这里插入图片描述
首先2号找到它子节点的最大值也就是5,也就是这样在这里插入图片描述
然后此时2号节点的子节点以遍历完毕,开始找父节点(此时我们假设该树除根节点以外都有一个指向父节点的指针),也就是找到了1号节点。在这里插入图片描述
然后1号节点的子节点中左节点已经遍历完毕并且没有父节点,故找到了右节点3号。在这里插入图片描述
然后3号由于父节点方面1号已经经过了,故开始对子节点6 7进行比较,7比较大故走右节点7。
在这里插入图片描述

我们可以看到,在这棵树上,这种方法确实是完美可行的,但是现在我们假设是下面这棵树结果会怎么样?在这里插入图片描述

没错,结点1变成了负数1,并且根节点没有了右子树,那么此时2到底是否走-1呢?
或许此时有人提出这样的一个想法,即这种时候由2判断没走过的两个结点即4和-1此时谁大,谁大走谁。在这里插入图片描述
此时返回最大值4+2+5=11,确实这样也解决了这个问题。但是假设在这棵树的基础下再改一下呢?
在这里插入图片描述
好的,由于我们走了4没有走-1从而丢失了走到100这个最大结点的路径。
倘若有人觉得我们可以回溯计算到100然后保存最大值的话,那么本质上与我文章前面所描述的方法一样。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值