我最近接受了采访,并被问到以下问题。
给定n-ary树,找到从根到叶的最大路径,使得最大路径不包含来自任何两个相邻节点的值。
(另一个编辑:节点只有正值。)
(从评论编辑:相邻节点意味着共享直接边缘的节点。因为它是一棵树,它意味着父子。所以如果我包括父,我不能包括子,反之亦然。)
例如:
5
/ \
8 10
/ \ / \
1 3 7 9
在上面的例子中,没有两个相邻的最大路径沿着路径5-> 10-> 9将是14。我在最后的总和中包括5和9但不包括10,因为它会违反没有两个相邻节点的条件。
我建议使用以下算法。虽然我对此非常肯定,但我的采访者似乎并不自信。因此,我想仔细检查我的算法是否正确。它似乎适用于我能想到的各种测试用例:
对于每个节点X,令F(X)为从根到X的最大和,没有最大总和中的两个相邻值。
计算公式F(X)= Max(F(父(X)),val(X)+ F(祖父(X)));
解决方案本来就是 解=最大(F(叶节点))
这大致就是我提出的代码:
class Node
{
int coins;
List<Node> edges;
public Node(int coins, List<Node> edges)
{
this.coins = coins;
this.edges = edges;
}
}
class Tree
{
int maxPath = Integer.MIN_VALUE;
private boolean isLeafNode(Node node)
{
int size = node.edges.size();
for(int i = 0; i < size; i++)
{
if(node.edges.get(i) != null)
return false;
}
return true;
}
// previous[0] = max value obtained from parent
// previous[1] = max value obtained from grandparent
private void helper(Node node, int[] previous)
{
int max = Math.max(previous[0], max.val + previous[1]);
//leaf node
if(isLeafNode(node))
{
maxPath = Math.max(maxPath, max);
return;
}
int[] temp= new int[2];
temp[0] = max;
temp[1] = prev[0];
for(int i = 0; i < node.edges.size(); i++)
{
if(node.edges.get(i) != null)
{
helper(node.edges.get(i), temp);
}
}
}
public int findMax(Node node)
{
int[] prev = new int[2];
prev[0] = 0;
prev[1] = 0;
if(node == null) return 0;
helper(node, prev);
return maxPath;
}
}
编辑:忘记提及我提出这个问题的主要目的是知道我的算法是否正确而不是要求新的算法。
编辑:我有理由相信我的算法也应该有效。
我在互联网上搜索类似的问题并遇到了这个问题: https://leetcode.com/problems/house-robber/?tab=Description
它与上面的问题非常相似,只是它现在是一个数组而不是树。
正式F(X)= Max(F(X-1),a [x] + F(X-2))适用于这种情况。
这是我接受的代码:
public class Solution {
public int rob(int[] nums) {
int[] dp = new int[nums.length];
if(nums.length < 1) return 0;
dp[0] = nums[0];
if(nums.length < 2) return nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++)
{
dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]);
}
return dp[nums.length-1];
}
}
答案 0 :(得分:2)
自然解决方案是为每个节点X计算两个值:从X到叶子的最大路径,包括从X到叶子的X和最大路径,不包括X,让他们称之为MaxPath(X)和MaxExcluded( X)。
对于叶子L MaxPath(L)是值(L),MaxExcluded(L)是0。
对于内部节点X:
MaxPath(X) = Value(X) + Max over child Y of: MaxExcluded(Y)
MaxExcluded(X) = Max over child Y of : Max(MaxExcluded(Y), MaxPath(Y))
第一行表示如果包含X,则必须排除其子项。第二个意味着如果您排除X,您可以自由地包含或排除其子项。
它是节点上的一个简单的递归函数,可以通过O(树的大小)中的叶子到父节点来计算。
编辑:递归关系也可以自上而下运行,在这种情况下,您确实可以通过MaxExcluded(Y)
实际为MaxPath(Parent(Y))
的观察来消除存储两个值,这给出了在问题
答案 1 :(得分:0)
@RafałDowgird解释的实现。
/* 5
* 8 10
* 1 3 7 9
* 4 5 6 11 13 14 3 4
*
*
*/
public class app1 {
public static void main(String[] args) {
Node root = new Node(5);
root.left = new Node(8);root.right = new Node(10);
root.left.left = new Node(1);root.left.right = new Node(3);
root.right.left = new Node(7);root.right.right = new Node(9);
root.left.left.left = new Node(4);root.left.left.right = new Node(5);
root.left.right.left = new Node(6);root.left.right.right = new Node(11);
root.right.left.left = new Node(13);root.right.left.right = new Node(14);
root.right.right.right = new Node(4);
System.out.println(findMaxPath(root));
}
private static int findMaxPath(Node root) {
if (root == null) return 0;
int maxInclude = root.data + findMaxPathExcluded(root);
int maxExcludeLeft = Math.max(findMaxPath(root.left), findMaxPathExcluded(root.left));
int maxExcludeRight = Math.max(findMaxPath(root.right), findMaxPathExcluded(root.right));
return Math.max(maxInclude, Math.max(maxExcludeLeft, maxExcludeRight));
}
private static int findMaxPathExcluded(Node root) {
if(root == null) return 0;
int left1 = root.left!=null ? findMaxPath(root.left.left) : 0;
int right1 = root.left!=null ? findMaxPath(root.left.right) : 0;
int left2 = root.right!=null ? findMaxPath(root.right.left) : 0;
int right2 = root.right!=null ? findMaxPath(root.right.right) : 0;
return Math.max(left1, Math.max(right1, Math.max(left2, right2)));
}
}
class Node{
int data;
Node left;
Node right;
Node(int data){
this.data=data;
}
}