目录
Leetcode - 235
三种情况,若p,q的值同时小于root的值,那么p,q肯定存在于root的左子树里,且其公共祖先肯定也存在于左子树里,若p,q值同时大于root的值,那么p,q肯定存在于root的右子树里。若root的值存在于p,q之间,那么root肯定是p,q的最近祖先,为什么呢?因为此时,无论root是向左子树还是右子树继续遍历那么一定会漏掉p,q其中一个,所以root肯定为最近祖先
递归 有返回值,最近祖先
递归终止,root为None,返回root。
此题不涉及前中后序遍历,这是由于二叉搜索树的特性,第一种情况就递归左子树,第二种情况就递归右子树,第三种情况就直接返回
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
def traversal(root,p,q):
if not root:
return root
if root.val < p.val and root.val < q.val:
right = traversal(root.right,p,q)
#其实这里,包括底下,都不用加判断,因为公共祖先是一定存在的,且肯定是在这个子树里
if right:return right
elif root.val > p.val and root.val > q.val:
left = traversal(root.left,p,q)
if left:return left
else:
return root
return traversal(root,p,q)
不用递归也可以,写一个while循环,在root不为None的情况下,分为三种情况讨论,root值小就向右,否则向左,root在中间就直接返回
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
while root:
if root.val < p.val and root.val < q.val:
root = root.right
elif root.val > p.val and root.val > q.val:
root = root.left
else:
return root
Leetcode - 701
这题的暴力法和递归我都是一口气写出来的,想清楚就好,这题关键就在于找叶子节点,然后插入,root值小就往右,否则往左,若左子树右子树为空,就直接加入节点。
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if not root:
return TreeNode(val)
#因为root最终要到叶子节点的位置,此处要像链表一样设置一个虚拟头结点,指向原来的根节点
pre = TreeNode()
pre.left = root
while root:
if root.val > val:
if root.left == None:
root.left = TreeNode(val)
break
root = root.left
elif root.val < val:
if root.right == None:
root.right = TreeNode(val)
break
root = root.right
return pre.left
递归法:
有返回值
终止条件:root为None, 此时说明到了根节点的下一个节点,此处返回要添加的节点即可,因为这里回溯到上一层就是叶子节点,返回这个节点作为叶子节点的孩子。
若root的值小,则递归右子树,根据终止条件,若此时返回值不为None,则说明root为叶子节点,root.right = 递归的返回值即可
若root值大,同理若左子树递归结果不为None,将递归结果作为root的左子节点
因为递归回返回到根节点,最后返回root即可。
def insert(root,val):
if not root:
return TreeNode(val)
if root.val > val:
left = insert(root.left,val)
if left:
root.left = left
if root.val < val:
right = insert(root.right,val)
if right:
root.right = right
return root
return insert(root,val)
Leetcode - 450
这题说实话,不听讲解我是肯定写不出来的hh
递归: 返回值为新树的根节点
终止条件,这里有大量的处理逻辑,以及可能会返回子树,这里其实和701很像,都是添加或删除节点,是对树做结构上的变化,在这种题目里终止条件会返回节点或者子树一类的,因为这里会作为回溯的结果返回给上一层节点,作为其左子树或者右子树
这里分五种情况讨论:1. root为None,即没有找到要删除的节点,直接返回None,2.找到了,但是该节点为叶子节点,这里也返回None,因为删除该叶子节点,只需要将这个节点的父节点指向空即可。3.找到了,左子树为空,右子树不为空,则将右子树作为改节点父节点的子树即可, 这里返回root的右子节点4.找到了,左子树不为空,右子树为空,同理,返回左子树。5.左右子树均不为空,这里最难的情况,这里将左右子树根节点返回都可,这里以返回右子树为例,明确一点,要删除的节点的值是处于左右子树中间的,与其最相邻的值是左树根节点和右树最左边的节点,即右树的最小节点,所以这里是找到右树最左节点,然后让root的左子树作为他的左子树即可,最后返回root.right。
接着是递归的过程,若root值小,则递归右子树,右子树的返回结果直接作为root的新的右子树
若root值更大,则将左子树递归的结果作为root的新的左子树。因为值不等的时候会一直递归,直到满足相等时,才会有相应的新树返回给上一层,这里的上一层就是要删除节点的父节点。所以这里直接用root.left或者root.right接收返回值是合理的,此时root就是父节点。
因为递归最后要回溯到根节点,所以最后返回root即可
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
def delete(root,key):
if root == None:
return root
if root.val == key:
if root.left == None and root.right == None:
return None
elif root.left == None and root.right != None:
return root.right
elif root.left != None and root.right == None:
return root.left
else:
cur = root.right
#这里一直向左遍历,直到左子树为空,就找到了最左的叶节点
while cur.left:
cur = cur.left
cur.left = root.left
return root.right
if root.val < key:
root.right = delete(root.right,key)
if root.val > key:
root.left = delete(root.left,key)
return root
return delete(root,key)