【问题标题】: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

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

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

【问题讨论】:

    标签: java data-structures tree binary-tree reverse


    【解决方案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;
        }
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-03-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多