https://oj.leetcode.com/problems/symmetric-tree/
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
For example, this binary tree is symmetric:
1 / \ 2 2 / \ / \ 3 4 4 3
But the following is not:
1 / \ 2 2 \ \ 3 3
Note:
Bonus points if you could solve it both recursively and iteratively.
解题思路:
要验证一个二叉树是不是轴对称的,我们可以直接考虑它的左右是否对称。但是好像一时半会儿想不出方法。于是想到另一个不是那么直接的方法,就是将这个树以轴做镜像变换,变换后的结果和原来的树相同,那么它就是轴对称的。
于是,这个问题可以化为两个问题,第一,如何将一个树做轴镜像变换,第二,如何判断变换后的两棵树相等。
第一个问题,将根节点的左右子节点交换,然后对它的左右子树也做这样的操作,这是一个递归的方法。第二个问题,仍然可以用递归的方法判断两棵树是否相等。但是,考虑到对root做了镜像后,root就不存在了,除非对原来的树做一个一模一样的拷贝,然后再比较它们是否相等,这样比较难。现在考虑另一个思路,将原来的树存为一个唯一的序列化表现形式,用这种方式将一棵树存下来,然后只要比较原树和变换后的树的序列化形式就可以了。这个问题在 Same Tree 的问题里讨论过,只有前序和层次遍历可以对一棵树序列化,Anagrams 这道题也是同样的思路。代码如下。
/** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class Solution { public boolean isSymmetric(TreeNode root) { StringBuffer result_p = new StringBuffer(); StringBuffer result_q = new StringBuffer(); preOrder(root, result_p); mirrorTree(root); preOrder(root, result_q); return result_p.toString().equals(result_q.toString()); } public void mirrorTree(TreeNode root){ if(root == null){ return; } TreeNode leftNode = root.left; root.left = root.right; root.right = leftNode; mirrorTree(root.left); mirrorTree(root.right); } public void preOrder(TreeNode root, StringBuffer result){ if(root == null){ result.append("#"); return; } result.append(root.val); preOrder(root.left, result); preOrder(root.right, result); } }
这个方法用了两次递归,当然序列化的方法preOrder是可以迭代的。考虑一下有么有其他更直接的方法?
我们再来看看轴对称的定义。一棵树是轴对称的,那么它的左右子树一定是对称的。要注意,这里将判断一棵树是否关于轴对称的问题,转化为判断两棵树是否为轴对称的树的问题,这是一个与原题相似却不同的问题。这样,虽然原题不太好直接写成一个递归的方法,但是,判断两棵树轴对称,这就是一个递归的问题了。
A、B树互为轴对称,那么他们的根节点一定相等,而且A的左子树和B的右子树一定互为轴对称,A的右子树和B的左子树也互为轴对称。这就是一个递归的定义了。代码如下。
/** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class Solution { public boolean isSymmetric(TreeNode root) { if(root == null){ return true; } return isMirrorTree(root.left, root.right); } public boolean isMirrorTree(TreeNode p, TreeNode q){ if(p == null && q == null){ return true; } if(p != null & q != null && p.val == q.val){ return isMirrorTree(p.left, q.right) && isMirrorTree(p.right, q.left); }else { return false; } } }
我们看到,这个解法的巧妙之处在于,考虑一棵树是否轴对称不容易转化为递归,但是考虑两棵树是否互为轴对称就容易多了。
原题还要求一个迭代的解法。借助上面的思路,我们可以对原树进行level order的遍历,将每层的遍历结果保存成一个String[],对于null的节点,插入一个特殊的字符,比如“#”。这样,对每层的结果,判断它们是不是轴对称就可以了。
需要注意的有几个细节,一个是这个String[]数组的大小,和插入字符的下标。还有这个结果一定不能用StringBuffer,在后面直接append。否则遇到两位数,或者负数,加起来就无法判断是否轴对称了。要以某种形式将其分割才可以。
/** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class Solution { public boolean isSymmetric(TreeNode root) { Queue<TreeNode> queue = new LinkedList<TreeNode>(); if(root == null){ return true; } queue.add(root); while(queue.size() != 0){ int levelSize = queue.size(); //这里只能用String[],不能用StringBuffer //否则遇到1,-54,#,-54,1这种从两头开始无法比较是否对称 //数组的size要注意是levelSize * 2 String[] levelCode = new String[levelSize * 2]; while(levelSize > 0){ TreeNode temp = queue.poll(); if(temp.left != null){ queue.offer(temp.left); //要注意数组的下标 levelCode[levelSize * 2 - 1] = String.valueOf(temp.left.val); }else{ levelCode[levelSize * 2 - 1] = "#"; } if(temp.right != null){ queue.offer(temp.right); levelCode[levelSize * 2 - 2] = String.valueOf(temp.right.val); }else{ levelCode[levelSize * 2 - 2] = "#"; } levelSize--; } //判断levelCode是否轴对称 int start = 0; int end = levelCode.length - 1; while(start < end){ if(!levelCode[start].equals(levelCode[end])){ return false; } start++; end--; } } return true; } }