leetcode代码总结
1.动态规划问题
动态规划思想介绍
一.基本思想
一般来说,只要问题可以划分为规模更小的字问题,并且原问题的最优解中包含了子问题的最优解,则可以考虑用动态规划解决。动态规划的实质是分治思想和解决冗余。因此,动态规划是一种将问题实例分解为更小的/相似的子问题(最优子结构),并存储子问题的解,使得每个子问题只求解一次,最终获得原问题的答案,以解决最优化问题的算法策略。
最优子结构通常是一个递推式
DP=递推式(最优子结构)+重复子问题(由于不想循环求解,所以将子问题的解存起来)
与贪心法的关系:
1.与贪心法类似,都是将问题实例归纳为更小的、相似的子问题,并通过求解子问题产生一个全局最优解。
2.贪心法选择当前最优解,而动态规划通过求解局部子问题的最优解来达到全局最优解。
与递归法的关系:
1.与递归法类似,都是将问题实例归纳为更小的、相似的子问题。
2.递归法需要对子问题进行重复计算,需要耗费更多的时间与空间,而动态规划对每个子问题只求解一次。对递归法进行优化,可以使用记忆化搜索的方式。它与递归法一样,都是自顶向下的解决问题,动态规划是自底向上的解决问题。
递归问题——>重叠子问题——> 1.记忆化搜索(自顶向下的解决问题);2.动态规划(自底向上的解决问题)
198题.打家劫舍
题目描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
class Solution:
def rob(self,nums):
#递推式:dp[i]=max(nums[i]+dp[i-2],dp[i-1])
if len(nums)==1:
return nums[0]
dp=[0 for _ in range(len(nums))]
dp[0]=nums[0]
dp[1]=max(nums[0],nums[1])
for i in range(2,len(nums)):
dp[i]=max(nums[i]+dp[i-2],dp[i-1])
return dp[-1]
解析:
动态规划问题中,最重要的就是寻找那个递推式,该问题中子问题就是找到到每一家能得到的最多金钱数,dp是一个新的列表,用于存放每一家所能得到的最大金钱数,注意边界条件。
62题.不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右
- 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3
输出:28
示例 4:
输入:m = 3, n = 3
输出:6
class Solution:
def uniquePaths(self,m,n):
#递推式:res[i][j]=res[i-1][j]+res[i][j-1]
res=[[0 for _ in range(n)] for _ in range(m)]
for i in range(n):
res[0][i]=1
for i in range(m):
res[i][0]=1
for i in range(1,m):
for j in range(1,n):
res[i][j]=res[i-1][j]+res[i][j-1]
return res[-1][-1]
动态规划问题,找到递推式问题解决一大半。
64题.最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
示例 2:
输入:grid = [[1,2,3],[4,5,6]]
输出:12
class Solution:
def minPathSum(self, grid):
#递推式:grid[i][j]=min(grid[i-1][j],grid[i][j-1])+grid[i][j]
m,n=len(grid),len(grid[0])
for i in range(1,m):
grid[i][0]=grid[i-1][0]+grid[i][0]
for j in range(1,n):
grid[0][j]=grid[0][j-1]+grid[0][j]
for i in range(1,m):
for j in range(1,n):
grid[i][j]=min(grid[i-1][j],grid[i][j-1])+grid[i][j]
return grid[-1][-1]
70题.爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
class Solution:
def climbStairs(self,n):
#递推式:df[-1]=df[-2]+df[-3]
#所以直接ans.append(ans[-1]+ans[-2])
ans=[1,2,3,5]
while(len(ans)<n):
ans.append(ans[-1]+ans[-2])
return ans[n-1]
1143题.最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = “abcde”, text2 = “ace”
输出:3
解释:最长公共子序列是 “ace” ,它的长度为 3 。
示例 2:
输入:text1 = “abc”, text2 = “abc”
输出:3
解释:最长公共子序列是 “abc” ,它的长度为 3 。
示例 3:
输入:text1 = “abc”, text2 = “def”
输出:0
解释:两个字符串没有公共子序列,返回 0 。
#两个字符串m,n,假如它们最后一个字母相等,则最长字串相当于在m-1和n-1中取然后加1
#若它们最后一个字母不相等,则最长字串相当于在(m-1和n)或者(m和n-1)中取较大的
'''递推式:
0 若i=0 or j=0
dp[i,j]= dp[i-1][j-1]+1 若i,j>0且text1[i]==test2[j]
max(dp[i][j-1],dp[i-1][j]) 若i,j>0且text1[i]!=test2[j]
'''
'''
f g h i j k text1 len(text1)==6
0 1 2 3 4 5 6 j=1 2 3 4 5 6
0
a1
b2
c3
d4
e5
i=1 2 3 4 5
text2 len(text2)==5
'''
class Solution:
def longestCommonSubsequence(self,text1,text2):
dp=[[0 for _ in range(len(text1)+1)] for _ in range(len(text2)+1)]
for i in range(1,len(dp)):
for j in range(1,len(dp[0])):
if text1[j-1]==text2[i-1]: #对于字符串来说要减1才是下标号
dp[i][j]=dp[i-1][j-1]+1
if text1[j-1]!=text2[i-1]:
dp[i][j]=max(dp[i][j-1],dp[i-1][j])
return dp[-1][-1]
343题.整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。
class Solution:
def integerBreak(self,n):
#递推式:dp[i]=max(dp[j]*dp[i-j] for j in range(1,n//2+1)) 当i大于三时
if n<4:
if n==1 or n==2:return 1
if n==3:return 2
else:
dp=[0]*(n+1)
dp[0]=0
dp[1]=1
dp[2]=2
dp[3]=3
for i in range(4,n+1):
dp[i]=max(dp[j]*dp[i-j] for j in range(1,n//2+1))
return dp[-1]
121题.买股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution:
def maxProfit(self, prices):
#递推式:前i天的最大收益 = max(前i-1天的最大收益,第i天的价格-前i-1天中的最小价格)
min_p, earning = 999999, 0
for i in range(len(prices)):
min_p = min(min_p, prices[i]) #记录到第i天的中最小的那个买入点
earning = max(earning, prices[i]-min_p)
return earning
300题.最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
提示:
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
要设置一个dp数组,长度为nums长度,初始化全为1,表示其自己算作长度为1的子序列
class Solution:
def lengthOfLIS(self, nums):
dp=[1 for _ in range(len(nums))]
i=1
while(i<len(nums)):
res=0
for j in range(i):
if nums[i]>nums[j]:
res=max(res,dp[i]+dp[j])
if(res):
dp[i]=res
i+=1
return max(dp)
120.三角形最小路径和
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
示例 1:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
示例 2:
输入:triangle = [[-10]]
输出:-10
提示:
1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104
#动态规划,更新每一行
class Solution(object):
def minimumTotal(self, triangle):
n=len(triangle)
if n==1: #解决只有一个元素的特殊情况
return triangle[0][0]
row=1 #设置初始值行号为1
triangle[1][0]+=triangle[0][0] #将第一行上的元素分别加到第二行上去
triangle[1][1] += triangle[0][0]
while(row<n-1): #从第二行开始计算,并且最后一行不用计算
for i in range(0,len(triangle[row])):
if i==0:
triangle[row+1][0]+=triangle[row][i]
elif i==len(triangle[row])-1:
triangle[row+1][-1]+=triangle[row][i]
triangle[row + 1][i] = min(triangle[row][i - 1], triangle[row][i]) + triangle[row + 1][i]
else:
triangle[row+1][i]=min(triangle[row][i-1],triangle[row][i])+triangle[row+1][i]
row+=1
return min(triangle[-1])
2.贪心算法
贪心算法思想介绍
贪心算法思想:
顾名思义,贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路经问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
贪心算法的基本要素:
1.贪心选择性质。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
- 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
实现该算法的过程:
从问题的某一初始解出发;
while 能朝给定总目标前进一步 do
求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;
55题.跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
#贪心算法,每一次贪心的选择能跳到的步数中最远的
class Solution:
def canJump(self, nums: List[int]) -> bool:
n, rightmost = len(nums), 0
for i in range(n):
if i <= rightmost:
rightmost = max(rightmost, i + nums[i])
if rightmost >= n - 1:
return True
return False
#自己写的代码
#贪心算法,每一次贪心的选择能跳到的步数中最远的
class Solution:
def canJump(self, nums):
if nums[0]==0 and len(nums)!=1:
return False
if len(nums)<=2:
return True
index=0 #记录能够到达的下标位置
while(index<len(nums) and nums[index]+index<len(nums)-1):
maxnum=0 #记录能达到的位置中最大的数
if nums[index]==0:
return False #即使在while循环中,return也可以直接跳出整个函数
for i in range(1,nums[index]+1): #i是跳跃的步数,遍历得到从1到n步
num=nums[index+i]+index+i #能达到的位置
if num>len(nums)-1:
return True
if num>maxnum: #index记录开始的步数
maxnum=num
new=index+i #新的要到达的位置
index=new
return True
134题.加油站
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
示例 2:
输入:
gas = [2,3,4]
cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
leetcode代码:
class Solution:
def canCompleteCircuit(self, gas, cost):
def help(gas, cost):
oil = gas[0] - cost[0]
for i in range(1, len(gas)):
oil = oil + gas[i] - cost[i]
if oil < 0:
return False
return True
# 求可以出发的加油站,如果可以就进入help函数判断是否能回到原点
for i in range(len(gas)):
if gas[i] >= cost[i]:
gas_new = gas[i:] + gas[:i]
cost_new = cost[i:] + cost[:i]
res = help(gas_new, cost_new)
if res:
return i
return -1
将加油站写成类方法的形式:
class Solution:
def __init__(self,gas,cost,n):
self.gas=gas
self.cost=cost
self.n=n
#self.gas_new=[]
#self.cost_new=[]
#self.canCompleteCircuit()
#self.help()
def canCompleteCircuit(self):
#求start,可以出发的加油站
for i in range(self.n):
if self.gas[i]>=self.cost[i]:
self.gas_new=self.gas[i:]+self.gas[:i]
self.cost_new = self.cost[i:]+self.cost[:i]
res=self.help()
if res:
return i
return -1
def help(self):
oil = self.gas_new[0] - self.cost_new[0]
for i in range(1,self.n):
oil = oil + self.gas_new[i] - self.cost_new[i]
if oil < 0:
return False
return True
if __name__=="__main__":
gas = [5, 8, 2, 8]
cost = [6, 5, 6, 6]
n=len(gas)
s=Solution(gas,cost,n)
print(s.canCompleteCircuit())
调用加油站类:
import gas_station
gas = [5, 8, 2, 8]
cost = [6, 5, 6, 6]
n=len(gas)
s=gas_station.Solution(gas,cost,n)
print(s.canCompleteCircuit())
3.dfs深度优先搜索
深度优先搜索思想介绍
一.基本思想
leetcode相关题目
94.二叉树的中序遍历
给定一个二叉树的根节点 root ,返回它的 中序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
ans=[]
if root==None:
return []
def dfs(node):
if node==None:
return
help(node.left)
ans.append(node.val)
help(node.right)
help(root)
return ans
100.相同的树
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入:p = [1,2,3], q = [1,2,3]
输出:true
示例 2:
输入:p = [1,2], q = [1,null,2]
输出:false
示例 3:
输入:p = [1,2,1], q = [1,1,2]
输出:false
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q:
return True
elif not p or not q:
return False
elif p.val!=q.val:
return False
return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)
130.被围绕的区域
给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
示例 1:
输入:board = [[“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’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:
输入:board = [[“X”]]
输出:[[“X”]]
class Solution:
def solve(self, board):
h=set() #记录最外层没有被包围的O
h2=set() #记录与最外层相连的O
###########################找到最外层的O
for i in range(len(board)):
if board[i][0]=="O":
h.add((i,0))
if board[i][-1]=="O":
h.add((i,len(board[0])-1))
for i in range(len(board[0])):
if board[0][i]=="O":
h.add((0,i))
if board[-1][i]=="O":
h.add((len(board)-1,i))
##########################
# 找到所有和边界相连的O的位置并将它们添进h2集合中
def dfs(place,h2,board):
i=place[0]
j=place[1]
for (dx,dy) in [(i,j-1),(i,j+1),(i-1,j),(i+1,j)]:
if dx>0 and dy>0 and dx<len(board) and dy<len(board[0]) and board[dx][dy]=="O" and (dx,dy) not in h2:
h2.add((dx,dy))
dfs((dx,dy),h2,board)
for i in h:
dfs(i,h2,board)
h3=h|h2 #合并两个集合
#改变原列表
for i in range(len(board)):
for j in range(len(board[0])):
if board[i][j]=="O" and (i,j) not in h3:
board[i][j]="X"
104.二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
left_height=self.maxDepth(root.left)
right_height=self.maxDepth(root.right)
return max(left_height,right_height)+1
111.二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root: #处理特殊情况,当root为空时返回0
return 0
if not root.left and not root.right: #表示当前结点为叶子节点时
return 1 #当前结点有一层
min_depth = 10**9 #设置一个极大的数
if root.left: #存在左子树
min_depth = min(self.minDepth(root.left), min_depth)
if root.right:
min_depth = min(self.minDepth(root.right), min_depth)
#递归调用中不同层的的函数minDepth其参数min_depth的储存位置不同,所以内层的min_depth有变化外层的也没有变
return min_depth + 1 #每调用一次,说明有一个结点,深度加一
200.岛屿数量
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
示例 2:
输入:grid = [
[“1”,“1”,“0”,“0”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“1”,“0”,“0”],
[“0”,“0”,“0”,“1”,“1”]
]
输出:3
class Solution:
def numIslands(self,grid):
hang=len(grid)
lie=len(grid[0])
visit=set()
ans=0
def dfs(i,j):
for (dx,dy) in [(i,j-1),(i,j+1),(i-1,j),(i+1,j)]:
if dx>=0 and dy>=0 and dx<hang and dy<lie and grid[dx][dy]=="1"\
and (dx,dy) not in visit:
visit.add((dx,dy))
dfs(dx,dy)
for i in range(hang):
for j in range(lie):
if grid[i][j]=="1" and ((i,j) not in visit):
dfs(i,j)
ans+=1
return ans
547.省份数量
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
示例 1:
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
示例 2:
输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3
class Solution:
def findCircleNum(self,isConnected):
def dfs(i):
for j in range(provinces):
if isConnected[i][j] == 1 and j not in visited:
visited.add(j)
dfs(j)
provinces = len(isConnected)
visited = set()
circles = 0
for i in range(provinces):
if i not in visited:
dfs(i)
circles += 1
return circles
589.N叉树的前序遍历
给定一个 N 叉树,返回其节点值的 前序遍历 。
N 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。
进阶:
递归法很简单,你可以使用迭代法完成此题吗?
示例 1:
输入:root = [1,null,3,2,4,null,5,6]
输出:[1,3,5,6,2,4]
示例 2:
输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[1,2,3,6,7,11,14,4,8,12,5,9,13,10]
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def preorder(self, root: 'Node') -> List[int]:
ans=[]
def dfs(root):
if root==None:
return
ans.append(root.val)
for child in root.children:
dfs(child)
dfs(root)
return ans