基本框架
以dfs求深度为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution : def maxDepth (self , root ): def depth (node ): if node == None : return 0 a = depth(node.left) + 1 b = depth(node.right) + 1 return max (a,b) depth = depth(root) return depth
NC62 平衡二叉树 描述: 输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树 (Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
注:我们约定空树是平衡二叉树。
题解来自评论区
平衡二叉树_牛客博客 (nowcoder.net)
自顶向下的方法 先求出每一个节点的深度,然后用先序遍历的方式,来判断是不是平衡二叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution : def IsBalanced_Solution (self, pRoot ): note = dict () def depth (node ): if node == None : return 0 if node in note: return note[node] a = depth(node.left) + 1 b = depth(node.right) + 1 note[node] = max (a,b) return note[node] def judge (node ): if not node: return True return abs (note.get(node.left,0 )-note.get(node.right,0 ))<=1 and judge(node.left) and judge(node.right) depth(pRoot) return judge(pRoot)
自底向上的方式 然后对原始dfs的代码加以改造,如果不满足平衡二叉树的定义,则返回-1 ,并且如果左子树不满足条件了 ,直接返回-1,右子树也是如此,相当于剪枝,加速结束递归。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution : def IsBalanced_Solution (self, pRoot ): def dfs (node ): if node == None : return 0 a = dfs(node.left) if a == -1 : return -1 b = dfs(node.right) if b == -1 : return -1 sub = abs (a-b) if sub > 1 : return -1 return max (a,b)+1 res = dfs(pRoot) return res != -1
简化如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def height2 (self,root:TreeNode ): if not root: return 0 leftHeight = self.height2(root.left) rightHeight = self.height2(root.right) if leftHeight == -1 or rightHeight == -1 or abs (leftHeight-rightHeight) > 1 : return -1 else : return max (leftHeight,rightHeight) + 1 def isBalanced (self, root: TreeNode ) -> bool : return self.height2(root)>=0
NC16 判断二叉树是否对称
leetcode-100,101
递归 需要两个节点一起比较,如果两个节点都为空说明为对称节点。
如果一个为空说明不对称;
然后去比较两个节点值;
接着递归比较pq的外层,和内层
1 2 3 4 5 6 7 8 9 10 11 class Solution : def check (self,p:TreeNode,q:TreeNode ): if not p and not q: return True elif not p or not q: return False return p.val == q.val and self.check(p.left,q.right) and self.check(p.right,q.left) def isSymmetric (self , root ): return self.check(root,root)
队列 初始节点入队两次,每次提取两个结点并比较它们的值(队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像),然后将两个结点的左右子结点按相反的顺序插入队列中。**(兄弟节点都是诶着的,内层跟内层比,外层跟外层比)**
当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续n结点)时,该算法结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Solution : def isSymmetric (self , root ): if root == None : return True queue = list () queue.append(root) queue.append(root) while queue: p = queue.pop(0 ) q = queue.pop(0 ) if not p and q: return False elif p and not q: return False elif not p and not q: continue if p.val != q.val: return False queue.append(p.left) queue.append(q.right) queue.append(p.right) queue.append(q.left) return True
NC9 二叉树中是否存在节点和为指定值的路径 描述 给定一个二叉树和一个值$sum$,判断是否有从根节点到叶子节点的节点值之和等于$sum$ 的路径, 例如: 给出如下的二叉树,$sum=22$,
返回true,因为存在一条路径 $5\to 4\to 11\to 2$的节点值之和为 22。
解法 在原有的dfs基础上,增加一个全局变量,sumss
在遍历过程中sumss遇到没有子节点的节点就加一,然后递归遍历左右节点,
最后在减去当前节点的值,这里是回溯的思想
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution : def hasPathSum (self , root , sums ): self.flag = False if root == None : return self.flag self.sumss = 0 def depth (node:TreeNode ): if node == None : return self.sumss += node.val if not node.left and not node.right: if self.sumss == sums: self.flag = True depth(node.left) depth(node.right) self.sumss -= node.val depth(root) return self.flag
解法二
来自评论区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution : def hasPathSum (self , root , sums ): self.flag = False if root == None : return self.flag self.sumss = 0 def depth (node:TreeNode,target:int ): if node == None : return False target -= node.val if not node.left and not node.right: if target == 0 : return True return depth(node.left, target) or depth(node.right,target) return depth(root,sums)
NC11 将升序数组转换为平衡二叉搜索树 描述 给出一个升序排序的数组,将其转化为平衡二叉搜索树(BST).
示例
1 2 输入值:[-1,0,1,2] 返回值:{1,0,2,-1}
题解
来自牛客评论区
二叉搜索树,它的中序遍历后的结果就是一个升序的序列;
平衡的,说明任何一个节点的左子树和右子树的高度相差不超过1。
题目所给是一个升序数组,故大体流程就是根据一个中序遍历的序列,还原出对应的二叉树。
因为是中序遍历的结果,所以元素的左边都是以该元素为根节点的左子树上的元素,****元素的右边都是以该元素为根节点的右子树上的元素。
又因为是平衡的,所以先找出序列的中点,以中点的值生成根节点 ,他的左边右边相差不差过一个元素,即它的左子树和右子树的元素个数相差不超过一个,每次都安一样的策略来找根节点,即左右子树的高度相差不会超过1。
以中点的左边序列为一个新的序列,按照一样的方法生成一个平衡二叉树,该树是第一步找的根节点的左子树 。
以中点的右边序列为一个新的序列,按照一样的方法生成一个平衡二叉树,该树是第一步找的根节点的右子树 。
也就是把问题的规模拆分成一个一个的小规模问题,解法都是一样的,只是问题的规模不一样,到规模足够小的时候我们可以很方便的找出答案,再一步一步往上推回去,即可得到真个大规模问题的答案。
解法一:递归 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution : def sortedArrayToBST (self , num ): def dfs (left,right ): if left > right: return None mid = (left+right+1 ) // 2 root = TreeNode(num[mid]) root.left = dfs(left, mid-1 ) root.right = dfs(mid+1 ,right) return root return dfs(0 ,len (num)-1 )
解法二:用栈模拟递归
还是递归舒服
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Node : def __init__ (self,node:TreeNode,left:int ,right:int ): self.node = node self.left = left self.right = right class Solution : def sortedArrayToBST (self , num ): if len (num) == 0 : return None left = 0 right = len (num) - 1 mid = left + (right - left + 1 )//2 root = TreeNode(num[mid]) stack = list () stack.append(Node(root,left,right)) curRoot = root while left<right or len (stack) >0 : while left < right: right = mid - 1 mid = left+(right-left+1 )//2 leftNode = TreeNode(num[mid]) curRoot.left = leftNode curRoot = curRoot.left stack.append(Node(leftNode,left,right)) node = stack.pop() left = node.left right = node.right mid = left + (right-left+1 )//2 left = mid + 1 if left<= right: mid = left + (right - left + 1 )//2 curRoot = node.node rightNode = TreeNode(num[mid]) curRoot.right = rightNode curRoot = rightNode stack.append(Node(rightNode, left, right)) return root
NC5 二叉树根节点到叶子节点的所有路径和 描述 给定一个仅包含数字$0-9$ 的二叉树,每一条从根节点到叶子节点的路径都可以用一个数字表示。
例如根节点到叶子节点的一条路径是$1\to 2\to 3$,那么这条路径就用$123$ 来代替。
找出根节点到叶子节点的所有路径表示的数字之和
例如:
这颗二叉树一共有两条路径, 根节点到叶子节点的路径 $1\to 2$ 用数字$12$ 代替 根节点到叶子节点的路径 $1\to 3 $ 用数字$13$ 代替 所以答案为$12+13=25$
解法:DFS 和前面的套路一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution : def sumNumbers (self , root ): ress = [] temp = [] def dfs (node:TreeNode ): if node == None : return temp.append(node.val) if not node.left and not node.right: fuckyou = '' .join(map (str ,temp.copy())) ress.append(fuckyou) dfs(node.left) dfs(node.right) temp.pop() dfs(root) ress = map (int ,ress) return sum (ress)
解法:BFS
来自评论区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution : def sumNumbers (self , root:TreeNode ): if root == None : return 0 nodeQueue = list () valueQueue = list () res = 0 nodeQueue.append(root) valueQueue.append(root.val) while len (nodeQueue) > 0 : node = nodeQueue.pop() val = valueQueue.pop() if not node.left and not node.right: res += val else : if node.left != None : nodeQueue.append(node.left) valueQueue.append(val*10 + node.left.val) if node.right != None : nodeQueue.append(node.right) valueQueue.append(val*10 + node.right.val) return res
NC58 找到搜索二叉树中两个错误的节点 描述 一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树,请按升序输出这两个错误节点的值。(每个节点的值各不相同)
解法:中序遍历+查找 中序遍历加找异常位置的元素,
因为此时的情况必定是一个小的数放到了后面,一个大的数放到了前面所以从左往右找那个大的数,并记录下索引,从右往左找小的那个数,并且索引值必大于前面记录下的索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution : def findError (self , root ): nums = [] def dfs (node:TreeNode ): if node == None : return dfs(node.left) nums.append(node.val) dfs(node.right) dfs(root) ans = [] for i in range (len (nums)-2 ,-1 ,-1 ): if nums[i] > nums[i+1 ]: ans.append(nums[i+1 ]) break for i in range (1 ,len (nums)): if nums[i]<nums[i-1 ]: ans.append(nums[i-1 ]) break return ans
解法:递归+判断
见牛客评论区
NC12 重建二叉树
leetcode-106 依据后序遍历和遍历,构建二叉树
给定某二叉树的前序遍历和中序遍历,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
解法:递归 来自评论区
二叉树的前序遍历:根左右 ;中序遍历:左根右 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution : def reConstructBinaryTree (self, pre, vin ): if not pre: return None root = TreeNode(pre[0 ]) tmp = vin.index(pre[0 ]) root.left = self.reConstructBinaryTree(pre[1 :tmp+1 ], vin[:tmp]) root.right = self.reConstructBinaryTree(pre[tmp+1 :], vin[tmp+1 :]) return root
解法:双指针 见牛客评论区
NC109 岛屿数量 描述 给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。
岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。
示例1
1 2 3 [[1,1,0,0,0],[0,1,0,1,1],[0,0,0,1,1],[0,0,0,0,0],[0,0,1,1,1]] 3
解法:DFS 来自评论区
遍历整块大陆,横着竖着遍历都可以。
第一次碰到陆地的时候,就知道这是块岛屿了,所以将这块陆地放入探险队列,岛屿数量加一。
然后我们将这块岛屿的陆地探索完。 每一次将这块陆地周围(上下左右)的陆地放入队列,然后将这块陆地标记为已探索 (这里就直接置为’0’了)。
当探险队列为空时,表示这块岛屿的陆地全部被探索完了,我们继续寻找下一块陆地。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution : def solve (self , grid ): if grid == None or len (grid) == 0 : return 0 self.nr = len (grid) self.nc = len (grid[0 ]) def dfs (r:int ,c:int ): if r<0 or c<0 or r >= self.nr or c>=self.nc or grid[r][c] == '0' : return grid[r][c] = '0' dfs(r - 1 ,c) dfs(r + 1 ,c) dfs(r, c - 1 ) dfs(r, c + 1 ) num_islands = 0 for r in range (self.nr): for c in range (self.nc): if grid[r][c] == '1' : num_islands += 1 dfs(r,c) return num_islands
解法:BFS 从一个为1的根节点开始访问,每次向下访问一个节点,直到访问到最后一个节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution : def solve (self , grid ): if grid == None or len (grid) == 0 : return 0 self.nr = len (grid) self.nc = len (grid[0 ]) def bfs (i:int ,j:int ): queue = list () queue.append(i*self.nc + j) while len (queue) > 0 : id = queue.pop() r = id // self.nc c = id % self.nc if r-1 >= 0 and grid[r-1 ][c] == '1' : queue.append((r - 1 )* self.nc + c) grid[r-1 ][c] = '0' if r+1 < self.nr and grid[r+1 ][c] == '1' : queue.append((r+1 )* self.nc +c) grid[r+1 ][c] = '0' if c-1 >= 0 and grid[r][c-1 ] == '1' : queue.append(r * self.nc + c - 1 ) grid[r][c-1 ] = '0' if c+1 < self.nc and grid[r][c+1 ] == '1' : queue.append(r*self.nc + c + 1 ) grid[r][c+1 ] = '0' num_islands = 0 for r in range (self.nr): for c in range (self.nc): if grid[r][c] == '1' : num_islands += 1 bfs(r,c) return num_islands
NC60 判断一棵二叉树是否为搜索二叉树和完全二叉树
二叉搜索树,既二叉排序树
完全二叉树,设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数, 第 h 层所有的结点都连续集中在最左边
描述 给定一棵二叉树,已知其中的节点没有重复值,请判断该二叉树是否为搜索二叉树和完全二叉树。
解法 使用dfs进行中序遍历,获取升序的数组,如果数组中出现不是升序的,则说明不是搜索二叉树。
使用bfs进行层序遍历,来查看是否有不连续的叶子节点。这里具体的操作是不判断叶子节点是否为空,直接加入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution : def judgeIt (self , root ): self.flag1 = True self.flag2 = False self.nodeCount = 0 self.depth = 0 nums = [] def dfs (node:TreeNode ): if node == None : return 0 self.nodeCount += 1 dfs(node.left) nums.append(node.val) dfs(node.right) def bfs (): queue = list () queue.append(root) flag = False while len (queue)>0 : p = queue.pop(0 ) if p == None : flag = True continue if flag: return False queue.append(p.left) queue.append(p.right) return True dfs(root) for i in range (1 ,len (nums)): if nums[i] < nums[i-1 ]: self.flag1 = False self.flag2 = bfs() return [self.flag1,self.flag2]
NC138 矩阵最长递增路径 描述 给定一个 n行 m 列矩阵 matrix ,矩阵内所有数均为非负整数。
求一条路径,该路径上所有数是递增的。
这个路径必须满足以下条件:
对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。
你不能走重复的单元格。即每个格子最多只能走一次。
解法 参考牛客评论区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution : def solve (self , matrix ): self.rows = len (matrix) self.cols = len (matrix[0 ]) temps = [] paths = [ [0 for _ in range (self.cols)] for _ in range (self.rows) ] def dfs (i:int ,j:int ,pre:int ): if matrix[i][j] <= pre: return 0 if paths[i][j] > 0 : return paths[i][j] cur_max = 0 if i>0 : cur_max = max (dfs(i-1 ,j, matrix[i][j]), cur_max) if i < self.rows-1 : cur_max = max (dfs(i+1 , j, matrix[i][j]), cur_max) if j > 0 : cur_max = max (dfs(i, j-1 , matrix[i][j]), cur_max) if j < self.cols-1 : cur_max = max (dfs(i, j+1 , matrix[i][j]), cur_max) paths[i][j] = cur_max+1 return paths[i][j] max_ = 0 for i in range (self.rows): for j in range (self.cols): max_ = max (max_,dfs(i, j,-1 )) return max_
WC152 矩阵中的路径 描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如$\left[ \begin{array} { l l l l } a & b & c & e \ s & f & c & s \ a & d & e & e \end{array} \right]$,矩阵中包含一条字符串“bcced”的路径,但是矩阵中不包含“abcd”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
解法:回溯 来自评论区:【数据结构和算法】回溯算法解矩阵中的路径_牛客博客 (nowcoder.net)
采用回溯的思想。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Solution : ''' 可以在四个方向上查找,所以是一个四叉树 ''' def dfs (self,matrix,word,i,j,index ): ''' index:用于记录字符串迭代的位置 ''' if i >= self.rows or i <0 : return False if j >= self.cols or j <0 : return False if matrix[i][j] != word[index]: return False if index == len (word)-1 : return True temp = matrix[i][j] matrix[i][j] = "." res = False res = self.dfs(matrix,word, i + 1 , j, index + 1 ) res |= self.dfs(matrix,word, i - 1 , j, index + 1 ) res |= self.dfs(matrix,word, i, j + 1 , index + 1 ) res |= self.dfs(matrix,word, i, j - 1 , index + 1 ) matrix[i][j]=temp return res def hasPath (self , matrix , word ): if len (word) == 0 : return False self.rows = len (matrix) self.cols = len (matrix[0 ]) for i in range (self.rows): for j in range (self.cols): if self.dfs(matrix,word,i,j,0 ): return True return False
NC613 新集合-较难 描述 集合$s$中有整数 $1$ 到$n$,牛牛想从中挑几个组成一个新的集合。
现在牛妹给牛牛加了 $m$ 个限制,每个限制包含两个整数 $u$ 和 $v$ ($u\neq v$),且 $u$ 和 $v$ 不能同时出现在新集合中。
请问牛牛能组成的新集合多少种。
可以选0个数。
返回一个整数,既新集合的种类数。
解法:递归 来自评论区 题解 | #新集合#_牛客博客 (nowcoder.net)
根据限制数组,建立图的邻接矩阵
通过递归的方式枚举所有的集合
如果之前出现的数和当前数同时出现在图中,则不可行,直接返回。否则列出所有可行的集合,每列出一个集合,计数加一。
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution : def solve (self , n , m , limit ): graph = [[0 for _ in range (n+1 )] for _ in range (n+1 )] used = [False ] * (n+1 ) self.res = 0 for t in limit: x,y = t.x,t.y graph[x][y]=graph[y][x] = 1 print (graph) def dfs (idx ): if idx>n: self.res+=1 return dfs(idx+1 ) for j in range (idx): if used[j] and graph[idx][j] == 1 : return used[idx] = True dfs(idx+1 ) used[idx] = False dfs(1 ) return self.res