code100:same tree
Given two binary trees, write a function to check if they are the same or not.
Two binary trees are considered the same if they are structurally identical and the nodes have the same value.
Example 1:
Input: 1 1
/ \ /
2 3 2 3
[1,2,3], [1,2,3]
Output: true
Example 2:
Input: 1 1
/
2 2
[1,2], [1,null,2]
Output: false
Example 3:
Input: 1 1
/ \ /
2 1 1 2
[1,2,1], [1,1,2]
Output: false
解答:
根据之前涉及到深度优先广度优先算法可以想到有queue来求解,即先进先出。(heap则可以做到先进后出)
- 一般情况:先将root放入队列中,然后循环,当队列不为空的时候,get队列元素。两个队列元素进行判断:
1 如果都为None,则continue(表示同时到了边界节点)
2 如果有一个为None,则直接return False(一个到了边界节点,一个没到)这时 经过上面的筛选,这里直接用or即可表示条件。
3 如果.val不相等,直接return False(该点对应值不等)
4 如果遍历完成,则return Ture - 临界点:初始为空的情况。其实也可以直接放在一般情况中,因为会进入continue 然后遍历到空,return True。 初始一个为空,直接return False。
代码:
import queue
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
myqueue1 = queue.Queue()
myqueue2 = queue.Queue()
myqueue1.put(p)
myqueue2.put(q)
while not myqueue1.empty() and not myqueue2.empty():
a = myqueue1.get()
b = myqueue2.get()
#if a == b and (myqueue1):
if a == None and b ==None:
continue
if a == None or b == None:
return False
if a.val != b.val:
return False
else:
myqueue1.put(a.left)
myqueue1.put(a.right)
myqueue2.put(b.left)
myqueue2.put(b.right)
return True
PS:
对于这种需要一个个对比的情况,可以使用递归进行
理解为:一旦一环出现False,则返回为False (and的连接)
步骤:
每次进入递归,会出现以下情况:
1 两个全为None 则返回True(如果分支为这种情况,意味着该分支终结,返回True的话,会继续加and进行判断,意味着该分支的终结以及该分支没有问题)
2 其中一个为None,返回False(只要出现这种情况,最后的全局为False)
3 如果两个的.val相等,进入递归(这种情况意味着根节点本身有值且相等,进入其子节点进行考察)
4 如果两个的.val不相等,返回False
1 2 4 是递归的终止条件 3是递归的启动条件
代码:
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if p == None and q == None:
return True
if p == None or q==None:
return False
elif p.val != q.val:
return False
else:
return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)
需要思考:
递归与正向迭代的关系!!!
code94: Binary Tree Inorder Traversal
Given a binary tree, return the inorder traversal of its nodes’ values.
Example:
Input: [1,null,2,3]
1
2
/
3
Output: [1,3,2]
解答:
一般树的遍历都是可以用到队列或者堆这样的数据结构的
这里中序遍历,顺序为 左 中 右,考虑采用迭代的方法,使用堆(stack)进行 先进后出的操作。
1 因为迭代传入需要有堆进行传递,所以新建run方法,参数为 root(当前节点) stack。
2 对于python来说,堆 可以用 queue.LifoQueue()来表示。这是一个先进后出队列。
3 考虑迭代:
1) 迭代发生的条件1: 因为遍历顺序应该是先右再中再左。所以当存在右节点时,进入迭代。
2) 迭代发生的条件2:1中的迭代进行到最后应该是 遍历到 root节点。这时需要进入左子节点(如果存在)。同样进入左节点后先检查右节点的情况,存在则进入右节点,迭代。
3) 需要注意的是,不管迭代到哪一步 ,在结果回来的时候,都要在 右 的后面 接上 root.val。
简单地说,先进入右节点迭代,迭代完(或者没有迭代),导入root.val,然后进行左节点的迭代。这样的操作 会覆盖边界情况,如果没有子节点进行迭代时,仅仅返回 当前节点的val。
4 最后进行堆数据的获取。
代码:
import queue
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
a = queue.LifoQueue()
#heapq.heapify(a)
if root == None:
return []
else:
arr = []
'''
if root.right:
self.run(root.right, a)
a.put(root.val)
#heapq.heappush(a, root.val)
if root.left:
self.run(root.left, a)
'''
self.run(root,a)
while not a.empty():
arr.append(a.get())
return arr
def run(self, root, a):
if root.right:
self.run(root.right, a)
a.put(root.val)
if root.left:
self.run(root.left, a)
这里我们使用了后进先出队列的同时也使用了迭代。还有其他方法:
比如官网提供的直接使用stack的情况。直接用的循环。
(这个用法才是大多数树的遍历使用方式)
过程:
1 往stack中注入左节点(条件是左节点存在),每次进入下一层同时要将root.val存入stack中。
2 不存在左节点时,取出stack值,注入数组res中。同时将该值对应的节点的右节点注入stack中(如果右节点存在的话),同样要进行判断之前的两个判断。
3 有点类似于迭代的循环。
简单说就是:现将根节点置入 stack中,检测 如果存在左节点,则将其置入stack中,然后没有左节点了,(如果stack不为空) 就 pop stack值(如果空就退出),如果其存在右节点,就直接 push进stack。 然后再pop 依次循环做到最后。
但是实际操作中,根据是否有左右节点 来判断会有不对的地方。因为tree点不删除子节点的话,遍历过来都会出现再度循环的情况(再度进入左节点)
修改:
我们使用一个操作参数 operate。这个参数作为判断的条件。
1 我们的目的是,先将左放在stack,然后取出的时候将右放在stack。右内再进行左的操作。root节点取出后还要放置右节点。
2 一般情况:
1) 进入循环second,如果operate不为None(初始赋值为root)?放左节点?不行,因为左节点会空,所以放自己。 operate=operate.left。
2) 这层循环出来以后,遍历来到最左节点的left 为空。
3)取出该点,val放入res中。然后operate=operate.right(之所以这样,是为了进入的循环second中。如果右节点 存在左节点,当然是优先处理)。外层加一个循环first,如果stack不为空,就继续。
4)这样处理的话,就需要stack进入时就不为空。为了均衡,将循环first的条件修改为 stack不为空或者operate!=None。也就是stack以及operate均为空就停止运行。
代码:
import queue
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
a = queue.LifoQueue()
# heapq.heapify(a)
if root == None:
return []
else:
res = []
#a.put(root)
operate = root
while not a.empty() or operate != None:
while operate:
a.put(operate)
operate = operate.left
operate = a.get()
res.append(operate.val)
print(res)
operate = operate.right
return res
理解:
边界root为None依然可以带入。(不需要额外的判断)
对于循环:
操作点 进行 入栈(如果有值),没有值 就出栈一个元素(左) 给到 res;
操作点为出栈点向右移。右移没有值的话继续 出栈 赋值 右移。 有值的话, 入栈。
这样就实现了 左边全入栈 ,出栈的时候 右边有的话 再检查其左边 再全入栈 没有就直接 出栈。 所有的右节点 都被当做 root节点处理完成的(即没有右节点的root节点)
这个方法需要记住!!!很重要。