树:每一个节点有一个根植和一个包含所有子节点的列表。从图来看,树也可视为一个拥有N 个节点和N-1 条边的一个有向无环图。
二叉树:一种更为典型的树树状结构。如它名字所描述的那样,二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。

树的遍历 可参考力扣官网动画更好理解----- 都是从左子树到右子树,前中后遍历的区别可以理解为节点的位置
1)前序遍历:前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。(从上到下)
2)中序遍历:中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。
3)后序遍历:先遍历左子树,然后遍历右子树,最后访问树的根节点。(从下到上)
tip:当删除树中的节点时,删除过程将按照后序遍历的顺序进行。 也就是说,当你删除一个节点时,你将首先删除它的左节点和它的右边的节点,然后再删除节点本身。
方法:递归和迭代

  • 有两种通用的遍历树的策略:
    1)深度优先搜索(DFS,应用栈):采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。【包括前序遍历,中序遍历和后序遍历】
    2)宽度优先搜索(BFS:应用队列):按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。
    leetcode
1.二叉树的前序遍历(medium)

题目:给定一个二叉树,返回它的 前序 遍历。
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]

思路:前序遍历【根节点–》左子树–》右子树】
1)递归:就是依次输出根,左,右,递归下去
2)迭代:使用栈来完成,将根节点放入栈中,先把当前节点的值存取数组中,自顶向下地压入左子树,直至左子树遍历完。然后每次从栈中弹出元素【因为栈是先入后出的,所以弹出的元素是从底到顶】直至遍历完右子树则结束。

  #递归
  def preorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        def helper(root):
            if not root:
                return
            res.append(root.val)
            helper(root.left)
            helper(root.right)
        helper(root)
        return res
   #迭代
   def preorderTraversal(self, root: TreeNode) -> List[int]:
   		 res=[]
        stack=[]
        if not root:
            return res
        while stack or root:
            while root:
                stack.append(root)
                res.append(root.val)
                root = root.left          
            top = stack.pop()
            root =top.right
        return res       

2.二叉树的中序遍历(medium)

题目:给定一个二叉树,返回它的中序遍历。
在这里插入图片描述

思路:中序遍历【左子树–》根节点–》右子树】
1)递归:就是依次输出左,根,右递归下去
2)迭代:使用栈来完成,模拟递归的思路,不断地往左边走,当左边走不下去了,就打印节点,并转向右边,然后右边继续这个过程。

   #递归
  def inorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        def helper(root):
            if not root:
                return res
            helper(root.left)
            res.append(root.val)
            helper(root.right)
        helper(root)
        return res
  # 迭代
  def inorderTraversal(self, root):
		"""
		:type root: TreeNode
		:rtype: List[int]
		"""
		res = []
		stack = []
		while stack or root:
			# 不断往左子树方向走,每走一次就将当前节点保存到栈中
			while root:
				stack.append(root)
				root = root.left
			# 当前节点为空,说明左边走到头了,从栈中弹出节点并保存
			top = stack.pop()
			res.append(top.val)
			root = top.right
		return res
3.二叉树的后序遍历(hard)

题目:给定一个二叉树,返回它的后序遍历。
在这里插入图片描述

思路:直接套用先序遍历的栈写法即可,
因为先序是“根左右”而后序是“左右根”,所以模仿先序的迭代方法生成“根右左”再反转输出就是“左右根了”

    def postorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        if not root:
            return res
        stack=[]
        while root or stack:
            while root:
                stack.append(root)
                res.append(root.val)
                root=root.right
            top=stack.pop()
            root=top.left
        return res[::-1]
4.二叉树的深度(easy)

题目:输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

思路:1)利用DFS的思想,采用递归方法,从根结点自顶向下遍历,每个节点的深度与它左右子树的深度有关,且等于其左右子树最大深度值加上 1。
2)利用BFS,BFS结合队列来实现,每遍历一层,则深度加一 ,直到遍历完成,则可得到树的深度。

1)DFS
 def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0    #终止条件: 当 root​ 为空,说明已越过叶节点,因此返回 深度 0 。
        #本质上是对树做后序遍历
        res = max(self.maxDepth(root.left),self.maxDepth(root.right))+1
        return res
2)BFS       
  def maxDepth(self, root: TreeNode) -> int:
        queue= collections.deque([root])
        depth=0
        if not root:
            return depth
        
        while queue:      
            length = len(queue)				#当前层的节点数        
            for i in range(length):			#遍历当前层的所有节点             
                node = queue.popleft()
                if node.left:				#当前结点是否存在左节点
                    queue.append(node.left)
                if node.right:				 #当前结点是否存在右节点
                    queue.append(node.right)
                
            depth+=1						#每遍历完一层,深度加一
        return depth
5.二叉树的层次遍历(medium)

题目:给你一个二叉树,请你返回其按层序遍历得到的节点值。即逐层地,从左到右访问所有节点
在这里插入图片描述

思路:迭代–利用BFS,设置队列存贮结点(先进先出),depth表示第几层。设置res为列表保存节点值。第0层只包含根结点root。
初始化队列只包含一个结点root,计算当前层有多少个元素。将这些元素从队列中弹出【队列存的是结点】将当前包含的节点值存入res中【注意,每层的数存入一个列表内,所以res就是个嵌套了两个列表,res[depth]在结果中就是表示第几层】。
再判断当前节点是否有左右孩子节点,有则将其压入队列
进入一层depth+=1

from collections import deque
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        res = []
        if not root:
            return res
        queue =deque([root])
        depth = 0
        while queue:
            res.append([])                  #注意是两层列表嵌套
            length = len(queue)				#队列的长度就是当前层包含的结点的个数
            for i in range(length):
                node = queue.popleft()
                res[depth].append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            depth+=1
        return res
6.二叉树的层次遍历(easy)

题目:给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

思路:1)直接在上题基础上将结果反转 res[::-1]
2)在保存节点值时,每次都把当前层结果列表插入到res首端,其实用的方法还是上题的,只是处理节点值时不一样

class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]: 
        res = []
        if not root:
            return res
        queue = deque([root])
        while queue:
            layer=[]
            length = len(queue)
            for i in range(length):
                node = queue.popleft()
                layer.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.insert(0,layer)
        return res
7. 剑指 Offer 32 - I从上到下打印二叉树(medium)

题目:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印

思路:采用bfs,自顶而下,用队列存储每一层节点,从左到右遍历每一层节点时将其存下来,其实就是层次遍历

import collections
class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        queue = collections.deque([root])
        res = []
        if not root:
            return  []
        while queue: 
            node = queue.popleft()
            res.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return res
8. 剑指 Offer 32 - II从上到下打印二叉树 II(easy)

题目:从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

思路:与题7思路一致,只是多新建一个栈来存储每层的节点值。与上面题5一样的

import collections
class Solution:
    def levelOrder(self, root):
        if not root:
            return []
        queue = collections.deque([root])
        res = []
        while queue:
            length = len(queue)
            tmp = []
            for i in range(length):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(tmp)
        return res
9. 剑指 Offer 32 - III从上到下打印二叉树 III (medium)

题目:请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

思路:
1)层次遍历+倒序:将生成的偶数层的节点取反之后再存入(跟上题一致,只是将每层节点存后再处理)
2)层次遍历+队列:采用一个队列来存偶数层的节点,偶数层的节点每次都添加到队列首部,奇数的则每次新加的添加到队列尾部。(每层节点处理完在存入)

import collections 
class Solution:
#层次遍历+倒序
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        queue = collections.deque([root])
        res = []
        flag = 1
        while queue:
            length = len(queue)
            tmp = []
            for i in range(length):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            flag +=1
            if flag%2==0:
                res.append(tmp)
            else:
                res.append(tmp[::-1])
        return res
#层次遍历 + 队列
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        queue = collections.deque([root])
        res = []
        while queue:
            tmp = collections.deque()
            length = len(queue)
            for i in range(length):
                node = queue.popleft()
                if len(res) % 2 !=0:
                    tmp.appendleft(node.val)
                else:
                    tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            
            res.append(list(tmp))
        return res
10. 验证二叉搜索树(medium)

题目:给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

思路:采用中序遍历。先遍历左子树,再遍历根节点,最后遍历右子树。判断当前节点是否大于中序遍历的前一个节点,如果小于等于则说明不满足返回 false。否则继续遍历
tip:程序初始化时,上、下限分别为对应语言中正无穷和负无穷,python中使用float(’-inf’)和float(‘inf’)表示

from collections import deque
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        if not root:
            return True
        stack =[]
        tmp=float('-inf')		#表示前一个节点的值
        while stack or root:
            while root:
                stack.append(root)
                root= root.left
            top = stack.pop()
            if top.val <=tmp:	# 如果中序遍历得到的节点的值小于等于前一个 tmp,说明不是二叉搜索树
                return False
            tmp = top.val
            root = top.right
       
        return True

11.对称二叉树(easy)

题目:给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
在这里插入图片描述

思路:1)采用递归:比较left.left和right.right,left.right和right.left,当left和right不等,或者left和right都为空,为False直接跳出
2)迭代:利用队列,首先将根结点的左右子树存入队列,
依次从队列中拿出两个节点(left和right)进行比较,
然后依次将left的left节点和right的right节点,left的right节点和right的left节点放入队列,
判断条件同上

1)递归
 def isSymmetric(self, root: TreeNode) -> bool:
        if not root :
            return True
        def isSym (left,right):
            if (left == None and right == None):
                return True
            if (left == None or right == None):
                return False
            if left.val!=right.val:
                return False
            return isSym(left.left,right.right) and isSym(left.right,right.left)
        
        return isSym(root.left,root.right)
 2)迭代
 def isSymmetric(self, root: TreeNode) -> bool:
        if not root or not (root.left or root.right) :
            return True
        queue = [root.left,root.right]
        while queue:
            left = queue.pop(0)
            right = queue.pop(0)
            if not (left or right):
                continue
            if not (left and right):
                return False
            if left.val!=right.val:
                return False
            queue.append(left.left)
            queue.append(right.right)
            queue.append(left.right)
            queue.append(right.left)
          
        return True
12. 相同的树(easy)

题目:给定两个二叉树,编写一个函数来检验它们是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

思路:1)递归
当两个树都不存在时,为True,
若一个为空,一个不为空则为False,
当两树的节点值不同时为False
依次遍历两数的节点。
2)迭代
层次遍历的思想,利用队列将p和q树的结点依次存入,每次取出结点进行判断,直到遍历结束

1)递归:
  def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not p and not q:
            return True
        if (not q and p) or (not p and q ):
            return False
        if p.val != q.val:
            return False
        return self.isSameTree(p.right,q.right) and self.isSameTree(p.left,q.left)

2)迭代:  
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        def check(p,q):
            if not p and not q:
                return True
            if (not q and p) or (not p and q ):
                return False
            if p.val != q.val:
                return False
            return True

        queue = collections.deque([(p,q)])
        while queue:
            p,q = queue.popleft()
            if  not check(p,q):
                return False
            if p:
                queue.append((p.left,q.left))
                queue.append((p.right,q.right))
        return True
13.另一个树的子树(easy)

题目:给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
在这里插入图片描述

思路:判断t为s的子树,分为三种情况:
1)t 和 s 相同 【可用上题判断是否为相同的树】
2)t 是s 的左子树
3)t 是s 的右子树

class Solution:
    def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
        if not s and not t:
            return True
        if not s or not t :
            return False
        return self.isSubtree(s.right,t)  or self.isSubtree(s.left,t) or self.isSametree(s,t)
    
    def isSametree(self,s,t):
        if not s and not t:
            return True
        if not s or not t :
            return False 
        if s.val != t.val:
            return False
        return  self.isSametree(s.left,t.left) and self.isSametree(s.right,t.right)
14.路径总和(easy)

题目:给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
在这里插入图片描述

思路:【DFS】,用栈存储从根节点到当前节点的值,以及对应的节点。
如果该node是叶子节点,则和目标值sum比较, 相等返回True。
若所有叶子的Sum都不和目标值sum相等,返回False。
【BFS】使用两个队列,一个保存节点,一个保存节点的值。每层遍历时更新节点总值,直到叶子节点并节点总值与目标值一致时则返回True,否则返回False

#【DFS】
 def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root: return False
        stack = [(root,root.val)]   
        while stack:
            node,Sum = stack.pop()
            if node.left:
                stack.append((node.left, Sum +node.left.val))
            if node.right:
                stack.append((node.right, Sum +node.right.val))  
            if not node.left and not node.right and Sum == sum:
                return True
        return False
#【BFS】
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return  False
        queue = collections.deque([root])
        queue_val = collections.deque([root.val])
        while queue:
            node = queue.popleft()
            val = queue_val.popleft()
            if not node.left and not node.right:
                if val == sum:
                    return True
                continue
            if node.left:
                queue.append(node.left)
                queue_val.append(val + node.left.val)
            if node.right:
                queue.append(node.right)
                queue_val.append(val + node.right.val)
        return False   

15.从中序与后序遍历序列构造二叉树(medium)

题目:根据一棵树的中序遍历与后序遍历构造二叉树。
注意:你可以假设树中没有重复的元素。
在这里插入图片描述

思路:由后序遍历的最后一个节点确定当前树的root,查找root在中序遍历序列中的位置, 将中序遍历分为根结点的左右子树。重新得到子树的中序,后序遍历,依次递归给下一层.

def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        if not inorder:
            return None
        root = TreeNode(postorder[-1])
        i = inorder.index(root.val)
        root.left = self.buildTree(inorder[:i], postorder[:i])
        root.right = self.buildTree(inorder[i+1:], postorder[i:-1])

        return root
16.从前序与中序遍历序列构造二叉树(medium)

题目:根据一棵树的前序遍历与中序遍历构造二叉树。
注意:你可以假设树中没有重复的元素。
在这里插入图片描述

思路:由前序遍历可知第一个节点即为根结点,然后将此节点对应在中序节点的位置将中序遍历分为左右子树。后续与上题思路一致

def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if len(inorder) == 0:
            return None
        root  = TreeNode(preorder[0])  #前序遍历第一个值为根节点
        i = inorder.index(root.val)	   #因为没有重复元素,所以可以直接根据值来查找根节点在中序遍历中的位
        root.right = self.buildTree(preorder[i+1:],inorder[i+1:])
        root.left = self.buildTree(preorder[1:i+1],inorder[:i])
        return  root
17.二叉树的最近公共祖先(easy)

题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路:搬运–这个题解讲解的很好
1)祖先的定义: 若节点 p在节点 root 的左(右)子树中,或 p=root ,则称 root 是 p的祖先
2)最近公共祖先的定义: 设节点root 为节点 p, q 的某公共祖先,若其左子节点 root.left和右子节点 root.right 都不是 p,q 的公共祖先,则称 root 是 “最近的公共祖先” 。
若 root是 p, q 的 最近公共祖先 ,则只可能为以下情况之一:
p 和 q 在 root 的子树中,且分别在 root 的左、右子树中;
p = root ,且 q 在 root 的左或右子树中;
q = root,且 p 在 root 的左或右子树中;
——————————-----------------------
思路:通过递归对二叉树进行后序遍历,当遇到节点 p 或 q 时返回。从底至顶回溯,当节点 p, q 在节点 root 的异侧时,节点 root 即为最近公共祖先,则向上返回 root。
具体实现:采用递归的方法,
1.递归结束的条件是当越过叶节点,则直接返回 null ;当 root 等于 p, q ,则直接返回root ;
2.递推工作:
开启递归左子节点,返回值记为 left ;
开启递归右子节点,返回值记为 right ;
3.返回值: 根据 left和 right ,可展开为四种情况;
1)当 left和 right同时为空 :说明 root的左 / 右子树中都不包含 p,q,返回 null ;
2)当 left 和 right 同时不为空 :说明 p, q分别在root 左 / 右子树,因此 root 为最近公共祖先,返回 root;
3)当 left 为空 ,right 不为空 :p,q都不在 root 的左子树中,直接返回 right 。
具体可分为两种情况:
p,q 其中一个在 root的 右子树 中,此时 right指向 p(假设为 p );
p,q 两节点都在 root 的 右子树 中,此时的 right 指向 最近公共祖先节点 ;
4)当 left不为空 , right 为空 :与情况 3. 同理;

def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left and not right: return # 1.
        if not left: return right # 3.
        if not right: return left # 4.
        return root # 2. if left and right

18.二叉树的镜像

题目:操作给定的二叉树,将其变换为源二叉树的镜像。

思路:1)镜像就是左右对换,利用DFS,将每一层的左右子树互换。
2)递归遍历二叉树(BFS),递归的交换当前节点的左右子节点,再递归的交换当前节点的左节点,递归的交换当前节点的右节点

from collections import deque
class Solution:
	#迭代
	 def Mirror(self, root):
	        # write code here
	        queue = deque([root])
	        while queue:
	            node = queue.popleft()
	            if not node:
	                return None
	            if node.left:
	                queue.append(node.left)
	            if node.right:
	                queue.append(node.right)
	            node.left,node.right = node.right,node.left
	        return root
	    #递归
	   def Mirror(self, root):
	        if not root:
	            return root
	        root.left, root.right =root.right,root.left
	        self.mirrorTree(root.left)
	        self.mirrorTree(root.right)
	        return root
	       

19.将有序数组转换为二叉搜索树(easy)

题目:将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
在这里插入图片描述

思路:采用递归
升序排列的数组等同于二叉树的中序遍历(左–>中–>右),因为树是高度平衡的,因此先找到中间节点。从数组中切分出左右子树节点,以该节点左边的升序序列构建左子树,右边的构建右子树,最后就得到一个高度平衡的二叉树。

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums:
            return None
        mid = len(nums) //2          #找到中间节点
        l = nums[:mid]
        r = nums[mid+1:]
        node = TreeNode(nums[mid])	 #构建树
        node.left = self.sortedArrayToBST(l)
        node.right = self.sortedArrayToBST(r)
        return node

20. 有序链表转化为二叉搜索树(medium)

21.相同的数(easy)

题目:给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

思路:采用深度优先搜索的思路
1)如果两个二叉树都为空,则相同;
2)如果一个为空,一个不为空,则不同
3)两个都不空,则分别判断两个二叉树的左子树是否相同以及右子树是否相同

 def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:     
        if not p and not q:
                return True
        elif (not q and p) or (not p and q ):
            return False
        elif p.val != q.val:
            return False
        else:
            return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

22.恢复二叉搜索树(hard)

题目:二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。

思路:首先因为中序遍历的元素是递增的,所以我们只要找到两个前一节点大于当前节点的节点,将其交换就可以了。下面采用迭代的方式进行中序遍历

class Solution:
    def recoverTree(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        pre = TreeNode(float('-inf'))  #前一节点
        first = None				   #第一个节点
        second = None				   #第二个节点
        stack = []
        while root or stack :
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            if not first and pre.val > root.val:#找到第一个节点
                first = pre
            if first and pre.val>root.val:	#找到第二个节点
                second = root
            pre = root
            root = root.right
        first.val, second.val = second.val, first.val

23.被围绕的区域(medium)

题目:给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

思路:任何边界上的 ‘O’ 都不会被填充为 ‘X’,所以以边界上的0相连成区域的0 都不会被填充为X,因此可以先以每一个边界上的0为起点,标记与其相连成区域的0(假设为M),再遍历这个标记好的矩阵,针对被标记过的字母,将其还原为O【因为其与边界O相连】,未被标记的修改为X

 def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        if not board:
            return
        row = len(board)
        col =  len(board[0])

        def dfs(x, y):
            if not 0 <= x < row or not 0 <= y < col or board[x][y] != 'O':
                return
            board[x][y] = "M"
            dfs(x + 1, y)
            dfs(x - 1, y)
            dfs(x, y + 1)
            dfs(x, y - 1)
        
        for i in range(row):
            dfs(i, 0)
            dfs(i, col - 1)
        
        for i in range(col-1):
            dfs(0, i)
            dfs(row - 1, i)
        
        for i in range(row):
            for j in range(col):
                if board[i][j] == "M":
                    board[i][j] = "O"
                elif board[i][j] == "O":
                    board[i][j] = "X"

24.重建二叉树(medium)

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
在这里插入图片描述

思路:前序遍历:【根,左子树,右子树】,中序遍历:【左子树,根,右子树】
由前序遍历的根节点代入中序遍历中可以将树划分为左右子树。此时根节点在中序遍历中的索引即为左子树的长度,于是得到划分后的左右子树,新建一个以根节点为起始节点的树,不断递归左右子树,直到左右子树为空,则完成树的建立

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if len(preorder) == 0:
            return None
        cur_val = preorder[0] #根节点
        cur_index = inorder.index(cur_val)#根节点在中序遍历中的索引
        root = TreeNode(cur_val)
        left_pre = preorder[1:cur_index+1] 
        left_in = inorder[:cur_index]
        right_pre = preorder[cur_index+1:]
        right_in = inorder[cur_index+1:]
        root.left = self.buildTree(left_pre,left_in)  
        root.right = self.buildTree(right_pre,right_in)
        return root

25.平衡二叉树(easy)

题目:给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
在这里插入图片描述
返回 true 。

思路
1)构造一个求深度的函数,通过比较子树的左右子树的深度差是否小于等于1,来判断子树是否平衡。再递归判断当前子树的左右子树是否也是平衡树。
2)采用自底向上的递归,类似后序遍历,先判断左右子树是否平衡再判断已当前根节点为根的子树是否平衡。
若子树是平衡,则返回其高度,否则返回-1.其高度一定是大于等于0

1class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root: return True
        return abs(self.depth(root.left) - self.depth(root.right)) <= 1 and 
            self.isBalanced(root.left) and self.isBalanced(root.right)

    def depth(self, root): #这里就是求树的深度
        if not root: return 0
        return max(self.depth(root.left), self.depth(root.right)) + 1
 2class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def height(root):
            if not root:
                return 0
            left = height(root.left)
            right = height(root.right)
            if left == -1 or right==-1 or abs(left-right)>1:
                return -1
            else:
                return max(left,right)+1
        return height(root)>=0

26.树的子结构(medium)

题目:输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
在这里插入图片描述

思路:
1.在A树中找到B树根节点值相同的节点
2.以此节点为根节点,与B树作比较.
1)两树都为空,返回True
2)A树为空,B树不为空,返回False
3)两节点值相同,并且A包含了B,则返回True

class Solution:
    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
        def recur (a,b): #是否包含
            if a==None and b:
                return False
            if not b:
                return True
            return a.val== b.val and recur(a.left,b.left) and recur(a.right,b.right)
        
        if A==None or B==None:
            return False
        if A.val == B.val and recur(A,B):
            return True
        return self.isSubStructure(A.left,B) or self.isSubStructure(A.right,B)  #寻找相同的根节点

27.扫雷游戏(medium)

题目:给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。

思路:没玩过地雷的人看得是一脸懵逼。。
缕清下游戏规则:
1)当点击某个方块时,如果此方块是地雷,则直接结束游戏,改为‘X’
2)当为空方块‘E’时(没有相邻的地雷),在其周围8个方向搜索,
如果没有地雷将其标记为‘B’;有的话则计算其周围有多少个地雷
于是采用深度优先搜索,来遍历方块周围是否存在地雷。

 def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]:
        direction=[[-1,0],[-1,1],[0,1],[1,1],[1,0],[1,-1],[0,-1],[-1,-1]] 
        def search(x,y): #查看是否存在雷,如果存在的话有几个雷
            count=0
            for i,j in  direction:
                if 0<=x+i<=n-1 and 0<=y+j<=m-1:
                    if board[x+i][y+j]=='M':
                        count+=1
            return count
        def dfs(x,y): 
            if x<0 or x>n-1 or y<0 or y>m-1: #判断边界
                return 
            if board[x][y]!='E': 
                return 
            count=search(x,y) #看是否存在雷
            if count!=0:  #存在雷则截断
                board[x][y]=str(count) 
                return 
            else:
                board[x][y]='B'
                for i,j in  direction:
                    dfs(x+i,y+j)
        n=len(board)
        m=len(board[0])
        if board[click[0]][click[1]]=='M': #有可能一开始就踩雷
            board[click[0]][click[1]]='X'
            return board
        else:
            dfs(click[0],click[1])
            return board

28. 二叉搜索树的第k大节点(easy)

题目:给定一棵二叉搜索树,请找出其中第k大的节点
在这里插入图片描述

思路:由图可知采用中序遍历(左-根-右)是递增的序列。本题要求解第k个最大的节点,因此我们可以采用中序遍历的倒序,则找到其第k个节点就是第k个最大的节点。采用迭代的方法,当遍历到一个节点,k-=1,直到k=0即找到该节点就输出。

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:    
        stack = []
        while root or stack:
            while root:
                if not root:
                    return None
                stack.append(root)
                root = root.right
            tmp = stack.pop()
            k-=1
            if k==0:
                return tmp.val
            root = tmp.left
        return None

29.钥匙和房间(medium)

题目:有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。
在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0,1,…,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。
最初,除 0 号房间外的其余所有房间都被锁住。
你可以自由地在房间之间来回走动。
如果能进入每个房间返回 true,否则返回 false
在这里插入图片描述

思路:采用先序遍历,记录每一次从根节点到叶子节点的路径,将路径(这些节点)存在列表中,并计算这些节点值之和,如果与目标值一致时,就将这些节点存入结果列表res中。

class Solution:
    def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
        def dfs(n):
            nonlocal num
            num+=1
            vis.add(n)
            for i in rooms[n]:
                if i not in vis:
                    dfs(i)
            
        vis = set()
        num = 0
        length = len(rooms)
        dfs(0)
        return num==length

30.二叉树的所有路径(easy)

题目:给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
在这里插入图片描述

思路:采用深度优先搜索,考虑当前节点和孩子节点。从根节点开始遍历,并把遍历到的节点保存到路径中。
当遍历到当前节点不存在左右子节点时【叶子节点】,并把此路径保存下来。
该节点还存在左右节点,继续遍历该节点的左右节点。

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        def helper(root,path):
            if root:
                path+=str(root.val)
                if not root.left and not root.right:
                    res.append(path)
                else:
                    path +='->'
                    helper(root.left,path)
                    helper(root.right,path)
            return res

        res = []
        helper(root,'')
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值