LeetCode94 二叉树的中序遍历
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
reslist=[]
def inorder(root):
if not root:
return
inorder(root.left)
reslist.append(root.val)
inorder(root.right)
inorder(root)
return reslist
中序遍历的递归实现,和先序遍历、后序遍历非常类似。
ToDo:非递归实现
LeetCode102 二叉树的层次遍历
class Solution(object):
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
res=[] #2维数组
def dfs(root,depth):
if not root:
return
if depth>=len(res):
res.append([])
res[depth].append(root.val)
dfs(root.left,depth+1)
dfs(root.right,depth+1)
dfs(root,0)
return res
同一层(depth相同)的节点放到同一个list中,利用dfs实现,从root开始一直往‘左臂’前进,当depth大于等于二维数组长度,就对二维数组创建新的元素(一维数组),将节点值存入新创建的list,‘左下角’到底之后,递归栈回退,将右节点的值插入和它同一层的list中,然后回退,直到遍历完整个树,结果就保存到了二维数组中。本质上是一种‘先序’遍历。
LeetCode103 二叉树的锯齿形层次遍历
class Solution(object):
def zigzagLevelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
reslist=[]
def dfs(root,depth):
if not root:
return
if depth==len(reslist):
reslist.append([])
if depth%2==0:
reslist[depth].append(root.val)
else:
reslist[depth].insert(0,root.val)
dfs(root.left,depth+1)
dfs(root.right,depth+1)
dfs(root,0)
return reslist
锯齿形层次遍历和一般层次遍历区别是,需要对depth的奇偶性进行判断,若是偶数,则插入到list的末尾,反之插入到list的开头。
LeetCode101 对称二叉树
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
def check(node1, node2):
if not node1 and not node2:
return True
elif not node1 or not node2:
return False
if node1.val != node2.val:
return False
return check(node1.left,node2.right) and check(node1.right,node2.left)
return check(root, root)
方法很巧妙,定义辅助函数check,判断两个树node1、node2是否对称。当两树都是空树,对称返回True;反之至少一个非空,假如有一个是空,那么就是一空一非空,返回False;反之都是非空,但是值不相同,也返回False;反之值也相同,那么就看‘node1的左子树和node2的右子树是否对称’,以及‘node1的右子树和node2的左子树是否对称’,这两者必须同时满足。最后调用check,两个节点都设为root,仔细去体会。
LeetCode104 二叉树的最大深度
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
return max(self.maxDepth(root.left), self.maxDepth(root.right))+1
代码非常简单,若当前根节点为空,返回0,否则,返回当前根节点的左右孩子的深度较大值再加一。
LeetCode100 相同的树
class Solution(object):
def isSameTree(self, p, q):
"""
:type p: TreeNode
:type q: TreeNode
:rtype: bool
"""
if not p and not q:
return True
if p and q and p.val==q.val:
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
else:
return False
代码很简单,若两树都为空,则是相同的;若两树都不为空,且根节点值相同,则看两树的左孩子与左孩子,右孩子与右孩子是否各自相同,且二者必须都相同;其他情况都是不相同。
LeetCode98 验证二叉搜索树
class Solution(object):
lastV=-2**32
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root:
return True
if self.isValidBST(root.left):
if self.lastV<root.val:
self.lastV=root.val
return self.isValidBST(root.right)
return False
二叉搜索树的中序遍历是升序排序;因此,可以在中序遍历的过程中,查看是否满足升序,即当前元素是否总比上一个遍历的元素大,为此用lastV记录上一个遍历的值;(注意:空树一定是二叉搜索树,它作为递归退出条件)然后先看左子树是否是二叉搜索树,如果不是,直接返回False;反之如果是,再看当前节点值是否比lastV大,如果不是,也直接返回False;如果是,那么先记录当前值root.val给lastV,然后再看右子树是否是二叉搜索树。
LeetCode108 将有序数组转换为二叉搜索树
class Solution(object):
def sortedArrayToBST(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
def myfun(nums,l,r):
if l>r:
return None
mid=l+(r-l)//2
res=TreeNode(nums[mid])
res.left=myfun(nums,l,mid-1)
res.right=myfun(nums,mid+1,r)
return res
if nums==None or len(nums)==0:
return None
else:
return myfun(nums,0,len(nums)-1)
将有序数组变为高度平衡二叉搜索树,定义辅助函数myfun,对nums数组的下标从l到r构造二叉搜索树,以l和r的中间值mid作为根节点下标,(注意:当l大于r,即数组为空,返回空树),根节点的左子树用nums的l到mid-1构造得到,根节点的右子树用nums的mid+1到r构造得到,返回根节点即可。外层函数调用myfun,分别以数组边界下标0、len(nums)-1作为l、r即可。这样得到的一定是高度平衡的二叉搜索树。
LeetCode110 平衡二叉树
class Solution(object):
# 返回一个树的高度
def myfun(self,root):
if root == None:
return 0
return max(self.myfun(root.left), self.myfun(root.right)) + 1
def isBalanced(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if root==None:
return True
L = self.myfun(root.left)
R = self.myfun(root.right)
if abs(L - R) <= 1:
return self.isBalanced(root.left) and self.isBalanced(root.right)
else:
return False
判断是否是平衡二叉树,定义辅助函数myfun计算树的高度,然后从root开始,若为空树,则返回True,否则计算左右孩子的高度L、R,如果L、R的差的绝对值大于1,返回False,否则继续看左右孩子是否都是平衡二叉树,只有两者都是,原树才是平衡二叉树。
LeetCode111 二叉树的最小深度
class Solution(object):
def minDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def myfun(root):
if root==None:
return 0
if root.left==None:
return myfun(root.right)+1
elif root.right==None:
return myfun(root.left)+1
else:
return min(myfun(root.left), myfun(root.right))+1
dp=myfun(root)
return dp
最小深度,即根节点到最近叶子节点路径上的节点数量,比较简单,若为空树,返回0;否则若左孩子为空,就返回右孩子的最小深度加一;同理若右孩子为空,就返回左孩子的最小深度加一;否则就返回左右孩子的最小深度较小值加一。
LeetCode226 翻转二叉树
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
tmp1=self.invertTree(root.right)#这里不能直接赋值给root.left,因为会影响下面调用的root.left
tmp2=self.invertTree(root.left)#所以暂时用临时变量代替
root.left=tmp1#执行交换
root.right=tmp2#执行交换
return root
方法一: 这种方法比较容易理解,首先分别对右子树和左子树执行翻转得到根节点,然后将左右子树的根节点进行交换。
class Solution(object):
def invertTree(self, root):
"""
:type root: TreeNode
:rtype: TreeNode
"""
if not root:
return None
self.invertTree(root.left)
self.invertTree(root.right)
root.left,root.right=root.right,root.left
return root
方法二: 这种方法和第一种非常相似,但理解起来完全不一样。大的树依赖于小的子树的问题解决,从树的底层一直往上,每一层交换树的左右子树;代码形式有点类似‘后序遍历’,但是先左还是先右是没有关系的。可以跟踪一个节点的交换过程来理解此代码。
LeetCode230 二叉搜索树中第K小的元素
class Solution(object):
def findnodes(self,root):#以root为根的树的节点个数
if not root:
return 0
return self.findnodes(root.left)+self.findnodes(root.right)+1
def kthSmallest(self, root, k):
"""
:type root: TreeNode
:type k: int
:rtype: int
"""
leftN=self.findnodes(root.left)
if leftN+1==k:
return root.val
elif k<=leftN:
return self.kthSmallest(root.left,k)
else:
return self.kthSmallest(root.right,k-leftN-1)
利用二分法,首先定义辅助函数findnodes计算一个树的节点个数;然后用它计算左子树的节点个数leftN,如果k刚才等于leftN+1,那么根节点就是要找的节点,返回即可;否则若k小于leftN+1,那么在左子树找第k小的节点,否则k大于leftN+1.,那么在右子树找第k-leftN-1小的节点。
LeetCode235 二叉搜索树的最近公共祖先
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if (root.val-p.val)*(root.val-q.val)<=0:
return root
elif root.val<p.val and root.val<q.val:
return self.lowestCommonAncestor(root.right,p,q)
else:
return self.lowestCommonAncestor(root.left,p,q)
由于是二叉搜索树,可利用节点值的相对大小去查找;从根节点出发,如果两个节点的值,一个比root大,一个比root小,即分别在左右子树,那么最近公共祖先就是根节点;否则如果两个都比root大,那么就都在右子树,去右子树查找;否则就都在左子树,去左子树查找。
LeetCode236 二叉树的最近公共祖先
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root or p==root or q==root:
return root
left=self.lowestCommonAncestor(root.left,p,q)
right=self.lowestCommonAncestor(root.right,p,q)
if left and right:
return root
if left:
return left
else:
return right
由于是普通二叉树,不能使用节点的值,设计难度稍大些;当root为空,这里返回空,表示找不到或不存在最近公共祖先;当root为p、q中的任意一个,返回root,与空相对,表示p、q中至少有一个在root为根的树中存在;然后分别去左右子树上查找p、q的最近公共祖先,结果为left和right;其中left和right都有可能为空或非空;当left和right都不为空, 那么说明左右子树都至少出现了p、q其中之一,它们的最近公共祖先必然是root;反之,如果left不为空(此时right为空),那么p、q都在原问题的左子树上,它们的最近公共祖先就等价于在原问题的左子树求出的最近公共祖先,其实就是left;对于right不为空(此时left为空),情况类似。
LeetCode257 二叉树的所有路径
class Solution(object):
def binaryTreePaths(self, root):
"""
:type root: TreeNode
:rtype: List[str]
"""
strlist=[]
def btp(root,cur):
if not root:
return
if not root.left and not root.right:
strlist.append(cur+str(root.val))
return
btp(root.left,cur+str(root.val)+'->')
btp(root.right,cur+str(root.val)+'->')
btp(root,"")
return strlist
方法比较简单,用全局变量strlist数组存最后结果,定义辅助递归函数(以根节点和当前路径str作为参数,该函数无需返回值),对root进行遍历,遇到空节点退出,遇到叶子节点,将当前str拼接上当前节点值作为完整路径加入strlist数组,并结束;遇到非叶子节点,继续在该节点左右子树调用自身,同时将参数中的当前路径拼接上当前的根节点值。另外需要注意’->'符号的出现细节,即非叶子节点需要加,叶子节点不加。
LeetCode404 左叶子之和
class Solution(object):
count=0
def sumOfLeftLeaves(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def cal(root):
if not root:
return
if root.left and not root.left.left and not root.left.right:
self.count+=root.left.val
cal(root.left)
cal(root.right)
cal(root)
return self.count
思路比较简单,利用全局变量count记录左叶子之和,定义辅助递归函数(无需返回值),目的是在遍历过程中把所有左叶子之和加到count上;若root为空,直接退出;否则若左子树存在,且左子树是叶子节点(它的左右子树都不存在),将它的值加到count中;对左右子树调用自身;最后返回count
LeetCode450 删除二叉搜索树中的节点
class Solution:
#非常巧妙
def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
if not root:
return None
if root.val>key:
root.left=self.deleteNode(root.left,key)
elif root.val<key:
root.right=self.deleteNode(root.right,key)
else:
if not root.left or not root.right:
root=root.left if root.left else root.right
else:
cur=root.right
while cur.left:
cur=cur.left #右子树的最左下角,即右子树中最小的节点,代替root成为新的根节点
root.val=cur.val
root.right=self.deleteNode(root.right,cur.val)
#递归删除,要找的节点,其实已经知道是哪个节点
return root
首先若root为空,无法删除,直接返回;反之若key比当前根节点小,对左子树执行删除key后返回的树作为新的左子树;反之若key比当前根节点大,对右子树执行删除key后返回的树作为新的右子树;反之当前根节点就是要删除的key,那么最简单的情况,当不存在左右子树之一,则直接删除root,左右子树哪个存在就作为新的树根返回;反之就需要去右子树查找新的根节点值代替它,具体找到右子树的最左下角,将它的值代替root,然后对右子树递归删除最左下角值得到新的树,代替右子树根。
LeetCode513 找树左下角的值
class Solution:
def findBottomLeftValue(self, root: TreeNode) -> int:
self.res=0
self.maxV=-2**31
def dfs(root,depth):
if not root:
return
if not root.left and not root.right:
if self.maxV<depth:
self.maxV=depth
self.res=root.val
dfs(root.left,depth+1)
dfs(root.right,depth+1)
dfs(root,0)
return self.res
题目求最后一行最左边的节点值;定义辅助函数dfs对树进行先序遍历,在遍历过程中,最终确定该值。全局变量res为每一步计算的值,maxV为当前遍历过节点的最大深度,只有当前节点为叶子节点,且当前深度比maxV大(注意:不能等于),才去更新res的值,同时更新最大深度maxV;这样就能保证同一深度的右边节点,无法修改左边第一个节点设置的值;可以找个例子好好体会一下。
LeetCode538 把二叉搜索树转换为累加树
class Solution:
def __init__(self):
self.num=0
def convertBST(self, root: TreeNode) -> TreeNode:
if root:
self.convertBST(root.right)
root.val+=self.num
self.num=root.val
self.convertBST(root.left)
return root
这道题把BST变为累加树,要求每个节点的值变为大于等于它的节点值的和,所以应该把右子树的和加到root,再把root加到左子树(的每个节点),对左右子树做同样的事;对root判断,若root为空直接返回,否则先遍历右子树(遍历结束,右子树的和就是num的值),再把num加到root,那么root的值对左子树而言就是要加的值,然后遍历左子树即可。最后也是返回root,但是每个节点的值都被改变了。
LeetCode543 二叉树的直径
class Solution(object):
diameter=0
def diameterOfBinaryTree(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def count(root):#计算高度,取决于左右子树高度较高者
if not root:
return -1
left=count(root.left)+1
right=count(root.right)+1
self.diameter=max(self.diameter,left+right)#额外插入对全局变量的修改,直径即左右子树高度和
return max(left,right)
count(root)#计算root节点高度
return self.diameter #返回副产品
思路比较简单,题目中的直径可看成一个节点的左右子树高度之和,那么求最大直径,只需对每个节点遍历,事实上可以在求高度的代码中插入一句话用于比较和更新最大直径。最大直径就是计算原树高度的副产品。
LeetCode563 二叉树的坡度
class Solution(object):
def findTilt(self, root):
"""
:type root: TreeNode
:rtype: int
"""
self.result=0
def calsum(root):#求和
if not root:
return 0
sums=root.val
left=calsum(root.left)
right=calsum(root.right)
self.result+=abs(right-left)#副产品,加到全局变量中
return sums+left+right
calsum(root)
return self.result
根据题意,节点的坡度为该节点左子树的结点之和和右子树结点之和的差的绝对值,最终要计算整个树的坡度,即所有节点的坡度之和。事实上,整个树的坡度,可以在对原树计算求和的过程中间接得到(和LeetCode 543有点类似)。定义calsum函数对一个树求和,如果是空树,返回0;否则分别计算左右子树的和,最终返回左右子树的和与自身的总和;而该root节点的坡度就能根据定义直接得到,累加到全局变量result即可。
LeetCode572 另一个树的子树
class Solution:
def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
def judgeEqual(s,t):#判断两个树是否完全一样
if not s and not t:
return True
if not s or not t:
return False
return s.val==t.val and judgeEqual(s.left,t.left) and judgeEqual(s.right,t.right)
if judgeEqual(s,t):
return True
if s.left and s.right:
return self.isSubtree(s.left,t) or self.isSubtree(s.right,t)
elif s.left:
return self.isSubtree(s.left,t)
elif s.right:
return self.isSubtree(s.right,t)
判断t是否是s的子树结构,首先定义辅助函数judgeEqual判断两个树是否完全相同(包括树结构和节点值);如果两树完全一样,那么返回True;否则若s左右子树都不为空,那么就看t是否s的左右子树的子树,满足其一即可;否则s的哪个子树不空,就去判断那个子树是否包含t。整个代码是个双递归实现。
LeetCode617 合并二叉树
class Solution:
def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
if not t1 and not t2:
return None
elif t1 and t2:
t1.val+=t2.val
t1.left=self.mergeTrees(t1.left,t2.left)
t1.right=self.mergeTrees(t1.right,t2.right)
return t1
elif t1:
return t1
else:
return t2
此题比较简单,如果两树都为空,返回空;如果两树一个空一个非空,返回非空的那个;否则两树都不为空,将t2加到t1上,让t1的左子树为t1的左子树和t2的左子树合并后的树,让t1的右子树为t1的右子树和t2的右子树合并后的树,返回t1即可。
LeetCode653 两数之和 IV - 输入 BST
class Solution:
def findTarget(self, root: TreeNode, k: int) -> bool:
self.dataset=set()
def helper(root,k):#查看root树中是否存在一个节点的值和dataset的某个值相加为k
if not root:
return False
if k-root.val in self.dataset:
return True
self.dataset.add(root.val)
return helper(root.left,k) or helper(root.right,k)
return helper(root,k)
判断BST中是否有两数之和为k;用全局变量set集合来存遍历过的节点值,同时判断k与当前节点值的差是否在集合中,若是返回True,否则就看左右子树是否存在。
LeetCode654 最大二叉树
class Solution:
def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
def helper(nums):
if not nums:
return None
maxv=max(nums)
ind=nums.index(maxv)
root=TreeNode(maxv)
root.left=helper(nums[:ind])
root.right=helper(nums[ind+1:])
return root
return helper(nums)
利用数组构造最大二叉树,思路比较简单,若数组为空,返回空树;否则计算数组最大值的下标,以该最大值构造节点,同时令该节点的左子树为数组左侧构造得到,令该节点的右子树为数组右侧构造得到。
LeetCode429 N叉树的层序遍历
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
res=[] #2维数组
def dfs(root,depth):
if not root:
return
if depth>=len(res):
res.append([])
res[depth].append(root.val)
for x in root.children:#对所有孩子递归
dfs(x,depth+1)
dfs(root,0)
return res
N叉树的层次遍历,与二叉树的层序遍历非常类似,只需对后者稍加修改,把left、right的递归操作改为对所有孩子。
LeetCode559 N叉树的最大深度
class Solution(object):
def maxDepth(self, root):
"""
:type root: Node
:rtype: int
"""
if not root:
return 0
if not root.children:
return 1
return max(self.maxDepth(n)+1 for n in root.children)
N叉树的最大深度,与二叉树的非常相似,把对left、right求最大深度的操作改为对所有孩子,取所有孩子的较大深度加一。
LeetCode589 N叉树的前序遍历
class Solution:
def preorder(self, root: 'Node') -> List[int]:
if not root:
return []
queue=[root]
res=[]
while queue:
node=queue.pop()
res.append(node.val)
queue.extend(node.children[::-1])
return res
非递归实现,比较巧妙,注意节点的孩子进入队列必须为逆序。需要仔细体会,记住这种实现即可。另外注意:N叉树不存在中序遍历。
LeetCode590 N叉树的后序遍历
class Solution:
def postorder(self, root: 'Node') -> List[int]:
lists=[]
stack=[]
if not root:
return []
lists=[root]
while lists:
cur=lists.pop()
stack.append(cur)
if cur.children:
lists.extend(cur.children)
while stack:
cur=stack.pop()
lists.append(cur.val)
return lists
利用两个栈配合实现,数组lists和stack的pop方式是‘后进先出’的,本质上模拟了栈;同时充分利用已经分配的空间,对lists循环利用。可以找个例子仔细体会一下。
LeetCode701 二叉搜索树中的插入操作
class Solution(object):
def insertIntoBST(self, root, val):
"""
:type root: TreeNode
:type val: int
:rtype: TreeNode
"""
cur=root
pre=None
kind=0#默认为0,代表插入为left,1代表right
while cur:
if cur.val<val:
pre=cur
cur=cur.right
kind=1
elif cur.val>val:
pre=cur
cur=cur.left
kind=0
if pre and kind==0:
pre.left=TreeNode(val)
if pre and kind==1:
pre.right=TreeNode(val)
return root
代码比较简单,用cur记录当前节点,pre记录上一个遍历的节点,kind记录上一步是走的左子树还是右子树;从root出发,比较root值与要插入的值val的相对大小,直到叶子节点退出,过程中更新pre和kind;根据最后一步的pre和kind来决定将val插入到pre的左子树还是右子树。注意:最后pre一定指向叶子节点,所以可以直接给它设置左右孩子。
LeetCode783 二叉搜索树结点最小距离
class Solution(object):
minval=2**32
pre=None
def bst(self,root):
if root==None:
return
self.bst(root.left)
if self.pre!=None:
self.minval=min(self.minval,root.val-self.pre.val)
self.pre=root
self.bst(root.right)
def minDiffInBST(self, root):
"""
:type root: TreeNode
:rtype: int
"""
self.bst(root)
return self.minval
题目让求BST任意两节点差的最小值,可以对其遍历得到;设置全局变量minval为当前差的最小值,pre为上一步遍历的节点;进行中序遍历,每次计算root与pre的差,若比minval小,就去更新minval,每一步更新pre节点为root。
LeetCode951 翻转等价二叉树
class Solution(object):
def flipEquiv(self, root1, root2):
"""
:type root1: TreeNode
:type root2: TreeNode
:rtype: bool
"""
if not root1 and not root2:
return True
if not root1 and root2:
return False
if not root2 and root1:
return False
if root1.val != root2.val:
return False
if (self.flipEquiv(root1.left, root2.right) and self.flipEquiv(root1.right, root2.left)) or \
(self.flipEquiv(root1.left, root2.left) and self.flipEquiv(root1.right, root2.right)):
return True
else:
return False
判断两个树是否翻转等价,是比较经典的题,也比较简单;如果都空,那么是等价的;如果只有一个空,那么就不等价;如果都不空,但是值不相同,那么不等价;如果值也相同,那么就看root1的左子树与root2的左子树以及root1的右子树与root2的右子树是否翻转等价,或者,root1的左子树与root2的右子树以及root1的右子树与root2的左子树是否翻转等价。