遍历的做法
def isSymmetric(self, root: TreeNode) -> bool:
ls,rs=[],[]
if not root:
return True
ls.append(root.left)
rs.append(root.right)
while ls and rs:
lroot,rroot=ls.pop(),rs.pop()
if lroot and rroot:
if lroot.val==rroot.val:
ls.extend([lroot.left,lroot.right])
rs.extend([rroot.right,rroot.left])
else:
return False
else:
if not lroot==rroot:
return False
return False if ls or rs else True
要注意的地方有如下:
- 最好采用两边同时遍历,然后进行比较的方法,不要想着两边分别遍历然后分别记录下两边的遍历结果然后看是不是相同的,浪费空间(没有必要)
- 为什么不建议采用分别记录下两边的遍历结果然后看是不是相同的,因为笔者试了一下,发现如果用前序遍历是ok的,但是如果用中序遍历记录结果的话,会有错误,比如说:
->3
->2 ->None
1 ->3 ->2
->None
会发现中序遍历,两边得到的是一样的序列...
(由于记录了None,带来了干扰,但是为了排除整棵树都是同一个数字的非对称树带来的错误,必须记录None)
个人认为可能是因为前序和中序的性质有点不一样:前序反映了节点的父子关系,中序只反映了兄弟关系。
所以,我觉得跟前序很相似的后序也能做
联想到一个问题:前序、中序、后序任选两个,哪两个的组合可以完全重构一棵树?答案是 只有前序和后序的组合不能重构,因为他们都之反映了父子关系。【题外话了hh】
所以还是前序简单好写又不会出错嘤嘤嘤,以后再也不写中序和后序了(中后序的iterative的写法相对难,空间复杂度也比前序高呢TAT
- 这个代码可以改进的地方就是:当两个子节点都是None的时候是可以不用入栈的,不然时间至少会翻倍。理由和代码如下:
Slight optimization on the iterative solution: if the children of left and right are appended to stack without checking if they’re both None, then we would append two pairs of None for every leaf node. In a balanced binary tree, that’s twice the number of actual nodes and would doubles the run time (more or less). The following method allows append only if either child is a valid address. The runtime improved from 68 to 64ms, but the distribution beaten went from 68% to 94%.
简单来说,就是:平衡二叉树的叶子节点数量和非叶子节点的数量大致是相同的,所以砍下叶子节点的两个None子节点的入栈是可以省下一半时间的。
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
# stack used to hold the matching pairs
stack = []
# if root=None, or if root has no children, root is symmetric
if not root or (not root.left and not root.right):
return True
stack.append((root.left, root.right))
while len(stack):
# the order of node retrieval matters little here, because we only care about
# pair content and not the relative order of different pairs; queue is quicker
# at finding shallow discrepancy, where stack is quicker at finding deeper
# discrepancy
left, right = stack.pop()
# if left and right are not symmetric, return false
if not left or not right or (left.val != right.val):
return False
# only append if the corresponding pairs exist
if left.left or right.right:
stack.append((left.left, right.right))
if left.right or right.left:
stack.append((left.right, right.left))
return True
这样写,不用分两个栈,代码的可读性也更提升了(?
递归的做法
递归的做法因为更贴合我们思考问题的想法,所以相对简单些,就贴下代码:
class Solution:
def isSymmetric(self, root):
if root is None:
return True
else:
return self.isMirror(root.left, root.right)
def isMirror(self, left, right):
if left is None and right is None:
return True
if left is None or right is None:
return False
if left.val == right.val:
outPair = self.isMirror(left.left, right.right)
inPiar = self.isMirror(left.right, right.left)
return outPair and inPiar
else:
return False