扁平化二叉树为链表(Java)_为什么这个递归代码不起作用?

huangapple go评论71阅读模式
英文:

Flatten Binary Tree to Linked List (Java)_ why this recursive code not working?

问题

Leetcode问题如下:

描述
以前序遍历将二叉树展开为一颗伪“链表”。
在这里,我们使用TreeNode中的右指针作为ListNode中的下一个指针。

输入:{1,2,5,3,4,#,6}

输出:{1,#,2,#,3,#,4,#,5,#,6}

解释:

     1
    / \
   2   5
  / \   \
 3   4   6

1
\
 2
  \
   3
    \
     4
      \
       5
        \
         6

下面的代码不能返回预期结果,但是无法弄清楚原因:

public class Solution {
     
    public void flatten (TreeNode root){
       TreeNode lastNode = null;
       helper (root, lastNode); 
    }
    
    private void helper(TreeNode root, TreeNode lastNode){
        if (root == null){
            return; 
        }
        
        if(lastNode != null){
            lastNode.left = null; 
            lastNode.right = root; 
        }
        lastNode = root; 
        TreeNode right = root.right;
        helper(root.left, lastNode); 
        helper(right, lastNode); 
        
    }
}

测试结果:
输入
{1,2,5,3,4,#,6}
输出
{1,#,5,#,6}
预期结果
{1,#,2,#,3,#,4,#,5,#,6}

不理解为什么输出是{1,#,5,#,6},而不是预期的{1,#,2,#,3,#,4,#,5,#,6}。有人能解释一下吗?谢谢

英文:

Leetcode question as below:

Description
Flatten a binary tree to a fake "linked list" in pre-order traversal.
Here we use the right pointer in TreeNode as the next pointer in ListNode.

Input:{1,2,5,3,4,#,6}

Output:{1,#,2,#,3,#,4,#,5,#,6}

Explanation:

     1
    / \
   2   5
  / \   \
 3   4   6

1
\
 2
  \
   3
    \
     4
      \
       5
        \
         6

code below does not return expected, but could not figure out why:

public class Solution {
     
    public void flatten (TreeNode root){
       TreeNode lastNode = null;
       helper (root, lastNode); 
    }
    
    private void helper(TreeNode root, TreeNode lastNode){
        if (root == null){
            return; 
        }
        
        if(lastNode != null){
            lastNode.left = null; 
            lastNode.right = root; 
        }
        lastNode = root; 
        TreeNode right = root.right;
        helper(root.left, lastNode); 
        helper(right, lastNode); 
        
        
    }
}

test result:
Input
{1,2,5,3,4,#,6}
Output
{1,#,5,#,6}
Expected
{1,#,2,#,3,#,4,#,5,#,6}

Couldn't understand why the output will be {1,#,5,#,6} instead of expected of {1,#,2,#,3,#,4,#,5,#,6}. Can anyone explain?Thanks

答案1

得分: 2

你的代码基本写得很好,但存在一个小错误。在编写代码时,你假设 lastNode 在进行先序遍历时会更新为最后一个节点。但事实并非如此。

在递归调用返回到这行代码 helper(right, lastNode); 后,lastNode 变量仍然指向当前最后一个节点。

让我们举个例子。假设我们在节点 2,所以我们将 lastNode 更改为节点 2,然后调用它的左子节点。在执行 helper(root.left, lastNode); 这行代码之后,我们认为 lastNode 应该指向节点 3。但事实并非如此,它仍然指向节点 2。

让我们看看调试器对上述情景的输出:

扁平化二叉树为链表(Java)_为什么这个递归代码不起作用?

为了消除这个错误,我们应该在递归调用期间返回 lastNode

请参考下面的示例代码:

private TreeNode helper(TreeNode root, TreeNode lastNode){
        if (root == null){
            return lastNode;
        }

        if(lastNode != null){
            lastNode.left = null;
            lastNode.right = root;
        }
        lastNode = root;
        TreeNode right = root.right;
        lastNode =  helper(root.left, lastNode);
        lastNode =  helper(right, lastNode);
        return lastNode;
}

经过上述更改,结果将会是这样的:

扁平化二叉树为链表(Java)_为什么这个递归代码不起作用?

英文:

You have almost coded it well, but there's a small bug. While writing the code you are assuming that the lastNode is updated to the last node while visiting in pre-order. But that is not the case.

The lastNode variable is still pointing to the current last node, after the recursion call is back at this line helper(right, lastNode); .

Let us take an example. Suppose we are at node 2, so we change the lastNode to node 2 and then call its left child. After the helper(root.left, lastNode); line is executed, we believe that lastNode should point to node 3. But that is not the case, it's still pointing to node 2.

Let's see what the debugger says for the above scenario

扁平化二叉树为链表(Java)_为什么这个递归代码不起作用?

What should we do to remove this bug, Just return the lastNode during the recursion call.

See the example code below

private TreeNode helper(TreeNode root, TreeNode lastNode){
        if (root == null){
            return lastNode;
        }

        if(lastNode != null){
            lastNode.left = null;
            lastNode.right = root;
        }
        lastNode = root;
        TreeNode right = root.right;
        lastNode =  helper(root.left, lastNode);
        lastNode =  helper(right, lastNode);
        return lastNode;
}

After the above changes, the result is something like this

扁平化二叉树为链表(Java)_为什么这个递归代码不起作用?

答案2

得分: 1

private void helper(TreeNode root, TreeNode lastNode){
    if (root == null){
        return; 
    }
    
    if(lastNode != null){
        lastNode.left = null; // *** 1
        lastNode.right = root; // *** 1 
    }
    lastNode = root; 
    TreeNode right = root.right;
    helper(root.left, lastNode); // *** 2
    helper(right, lastNode); // *** 3
}

在第一部分,你将左子节点连接到该lastNode的右侧。
在第二部分,你调用左子节点,在此期间将执行第一部分。
在第三部分,你调用右子节点,在此期间将执行第一部分。

因此,当第二部分完成其工作时,通过调用第一部分,lastNode将已经具有右子节点。当调用第三部分并执行其自己的代码的第一部分时,它将覆盖第二部分中完成的工作。

你可能想要做的是在helper函数中返回"叶子",并将叶子用作lastNode(即节点4,而不是根节点)。

private TreeNode helper(TreeNode root, TreeNode leaf){
    if (root != null){
        if (leaf != null){
            leaf.left = null; // *** 1
            leaf.right = root; // *** 1 
        }
        leaf = root;
        TreeNode right = root.right;
        leaf = helper(root.left, leaf); // *** 2
        leaf = helper(right, leaf); // *** 3
    }
        
    return leaf; 
}
英文:
private void helper(TreeNode root, TreeNode lastNode){
        if (root == null){
            return; 
        }
        
        if(lastNode != null){
            lastNode.left = null; // *** 1
            lastNode.right = root; // *** 1 
        }
        lastNode = root; 
        TreeNode right = root.right;
        helper(root.left, lastNode); // *** 2
        helper(right, lastNode); // *** 3
    }

In section 1, you attach the left children to the right for this lastNode.
In section 2, you call for the left children, where section 1 will be executed.
In section 3, you call for the right children, where section 1 will be executed.

So when section 2 completes it's work, lastNode will have right children already by calling section 1. When section 3 is called and it performs its own section 1 of the code, it will overwrite the work done in section 2.

What you probably want to do is return the "leaf" in the helper function and use the leaf as the lastNode (i.e. Node 4, instead of root).

	private TreeNode helper(TreeNode root, TreeNode leaf){
	    if (root != null){
	        if (leaf != null){
	        	leaf.left = null; // *** 1
	        	leaf.right = root; // *** 1 
	        }
	        leaf = root;
	        TreeNode right = root.right;
	        leaf = helper(root.left, leaf); // *** 2
	        leaf = helper(right, leaf); // *** 3
	    }
	        
	    return leaf; 
	}

huangapple
  • 本文由 发表于 2020年8月22日 14:07:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/63533111.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定