反转二叉树(从左到右)[关闭]

Posted

技术标签:

【中文标题】反转二叉树(从左到右)[关闭]【英文标题】:Reverse A Binary Tree (Left to Right) [closed] 【发布时间】:2012-02-27 05:01:53 【问题描述】:

我在看面试问题,最近遇到一个问你如何反转一般二叉树的问题,比如从右到左翻转它。

例如,如果我们有二叉树

     6
   /   \
  3     4
 / \   / \
7   3 8   1

反转它会创建

     6
   /   \
  4     3
 / \   / \
1   8 3   7

可以看到新树是原树的镜像。

我还没有想出一个好的实现来解决这个问题。任何人都可以提供任何好的想法吗?

【问题讨论】:

【参考方案1】:

您可以使用递归。我们就地交换节点的左右子节点,然后对其子节点执行相同操作:

static void reverseTree(final TreeNode root) 
    final TreeNode temp = root.right;
    root.right = root.left;
    root.left = temp;
    
    if (root.left != null) 
        reverseTree(root.left);
    
    
    if (root.right != null) 
        reverseTree(root.right);
    

处理参数root可能为空的情况:

static void reverseTree(final TreeNode root) 
    if (root == null) 
        return;
    
    
    final TreeNode temp = root.right;
    root.right = root.left;
    root.left = temp;
    
    reverseTree(root.left);
    
    reverseTree(root.right);

【讨论】:

【参考方案2】:

在 C/C++ 中以 O(1) 反转二叉树。

struct NormalNode 
  int value;
  struct NormalNode *left;
  struct NormalNode *right;
;

struct ReversedNode 
  int value;
  struct ReversedNode *right;
  struct ReversedNode *left;
;

struct ReversedNode *reverseTree(struct NormalNode *root) 
  return (struct ReversedNode *)root;

【讨论】:

java 中的演员阵容如何工作?我想你在回答一个不同的问题。 他是对的,它不适合java通过,但与c/c++很好 这个答案让我笑了(以一种好的方式)。一定要喜欢黑暗的 C++ 魔法:D 这是否违反了 C++ 中严格的别名规则,因此是未定义的行为?【参考方案3】:

这个问题有几个有趣的部分。首先,由于你的语言是 Java,你很可能有一个通用的 Node class,像这样:

class Node<T> 
    private final T data;
    private final Node left;
    private final Node right;
    public Node<T>(final T data, final Node left, final Node right) 
        this.data  = data;
        this.left  = left;
        this.right = right;
    
    ....

其次,反转,有时称为反转,可以通过改变节点的左右字段来完成,或者通过创建一个 new 节点就像原始节点一样,但具有左右子节点“逆转。”前一种方法显示在another answer,而第二种方法显示在这里:

class Node<T> 
    // See fields and constructor above...

    public Node<T> reverse() 
        Node<T> newLeftSubtree = right == null ? null : right.reverse();
        Node<T> newRightSubtree = left == null ? null : left.reverse();
        return Node<T>(data, newLeftSubtree, newRightSubtree); 
    

不改变数据结构的想法是persistent data structures 背后的想法之一,非常有趣。

【讨论】:

不应该是右为左反,左为右反? 交换是在构建一个新节点时完成的:也就是说,这就是为什么右边在左边之前。不清楚是因为没有给出构造函数。 好电话,@amnn,感谢您的初始编辑。 Java 缺少命名参数有点令人遗憾,能够说 new Node(data=data, left=right?.reverse(), right=left?.reverse()) 会很棒,但 Java 还没有。我们也可以做new Node().withData(data).withLeft(right.reverse.......).withRight(left.reverse......) 等。我最终只是通过使用命名局部变量使reverse 方法更具可读性。【参考方案4】:

您可以递归地交换左右节点,如下所示;

// helper method
private static void reverseTree(TreeNode<Integer> root) 
    reverseTreeNode(root);


private static void reverseTreeNode(TreeNode<Integer> node) 
    TreeNode<Integer> temp = node.left;
    node.left   = node.right;
    node.right  = temp;

    if(node.left != null)
        reverseTreeNode(node.left);

    if(node.right != null)
        reverseTreeNode(node.right);

Java 演示代码

import java.util.LinkedList;
import java.util.Queue;

public class InvertBinaryTreeDemo 

    public static void main(String[] args) 

        // root node
        TreeNode<Integer> root  = new TreeNode<>(6);

        // children of root
        root.left               = new TreeNode<Integer>(3);
        root.right              = new TreeNode<Integer>(4);

        // grand left children of root
        root.left.left          = new TreeNode<Integer>(7);
        root.left.right         = new TreeNode<Integer>(3);

        // grand right childrend of root
        root.right.left         = new TreeNode<Integer>(8);
        root.right.right        = new TreeNode<Integer>(1);

        System.out.println("Before invert");
        traverseTree(root);

        reverseTree(root);

        System.out.println("\nAfter invert");
        traverseTree(root);
    

    // helper method
    private static void reverseTree(TreeNode<Integer> root) 
        reverseTreeNode(root);
    

    private static void reverseTreeNode(TreeNode<Integer> node) 
        TreeNode<Integer> temp = node.left;
        node.left   = node.right;
        node.right  = temp;

        if(node.left != null)
            reverseTreeNode(node.left);

        if(node.right != null)
            reverseTreeNode(node.right);
    

    // helper method for traverse
    private static void traverseTree(TreeNode<Integer> root) 
        Queue<Integer> leftChildren     = new LinkedList<>();
        Queue<Integer> rightChildren    = new LinkedList<>();

        traverseTreeNode(root, leftChildren, rightChildren);

        System.out.println("Tree;\n*****");

        System.out.printf("%3d\n", root.value);

        int count = 0;
        int div = 0;
        while(!(leftChildren.isEmpty() && rightChildren.isEmpty())) 
            System.out.printf("%3d\t%3d\t", leftChildren.poll(), rightChildren.poll());
            count += 2;
            div++;
            if( (double)count == (Math.pow(2, div))) 
                System.out.println();
                count = 0;
            
        

        System.out.println();
    

    private static void traverseTreeNode(TreeNode<Integer> node, Queue<Integer> leftChildren, Queue<Integer> rightChildren) 
        if(node.left != null)
            leftChildren.offer(node.left.value);

        if(node.right != null)
            rightChildren.offer(node.right.value);

        if(node.left != null) 
            traverseTreeNode(node.left, leftChildren, rightChildren);
        

        if(node.right != null) 
            traverseTreeNode(node.right, leftChildren, rightChildren);
        
    

    private static class TreeNode<E extends Comparable<E>> 

        protected E value;
        protected TreeNode<E> left;
        protected TreeNode<E> right;

        public TreeNode(E value) 
            this.value = value;
            this.left = null;
            this.right = null;
        

    


输出

Before invert
Tree;
*****
  6
  3   4 
  7   3   8   1 

After invert
Tree;
*****
  6
  4   3 
  1   8   3   7 

【讨论】:

【参考方案5】:

递归函数可以很简单,如下图:

public Node flipTree(Node node) 
    if(node == null) return null;

    Node left = flipTree(node.left);
    Node right = flipTree(node.right);

    node.left = right;
    node.right = left;

    return node;

【讨论】:

请不要只回答代码。至少说明它为什么能解决问题,并用文本或 cmets 解释代码,以便未来的读者能够理解它。 公平地说,投票最多的答案也是纯代码。话虽如此,在添加您自己的答案之前,值得查看现有答案。您的答案与投票最多的答案完全相同,因此一般来说,最好只投票赞成该答案,而不是添加您自己的重复答案。【参考方案6】:

我看到大多数答案都没有关注空指针问题。

public static Node invertBinaryTree(Node node) 

    if(node != null) 
        Node temp = node.getLeftChild();

        node.setLeftChild(node.getRightChild());
        node.setRigthChild(temp);

        if(node.left!=null)  
            invertBinaryTree(node.getLeftChild());
        
        if(node.right !=null) 
            invertBinaryTree(node.getRightChild());
        
    

    return node;

在上面的代码中,我们仅在根节点的左/右子节点不为空时进行递归调用。它是最快的方法之一!

【讨论】:

以上是关于反转二叉树(从左到右)[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

设计一个算法把二叉数的叶子结点按从左到右的顺序连成一个单链表,二叉树按ldchild-rchild方式存储,?

题解——二叉树哈夫曼编码的实现

求上到下打印出二叉树的每个节点三种题型

剑指:之字形打印二叉树

从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印

从上到下按层打印二叉树,每层打印顺序从左到右