- 第8天开始了,今天开始加油吧!
开源内容
DW&LeetCode_day7(62、70、78)
写在前面:
开源内容
学习大纲
62. 不同路径
题解:
70. 爬楼梯
题解:
78. 子集
题解:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 3:输入:m = 7, n = 3
输出:28
示例 4:输入:m = 3, n = 3
输出:6
提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 109来源:力扣(LeetCode)
链接:题目链接题解:
Python中的Math库包含许多数学运算,可以使用该模块轻松执行。
math.comb()
Python中的method方法用于获取从n个项目中选择k个项目(不重复且无顺序)的方法数量。它本质上评估为n! /(k!*(n-k)!)它也被称为二项式系数,因为它等效于表达式(1 + x)的多项式展开中的k-th项的系数n。
用法: math.comb(n, k)
参数:
- n:非负整数
- k:非负整数
返回:一个整数值,表示从n个项目中选择k个项目(无重复且无顺序)的方式数量。
class Solution: def uniquePaths(self, m: int, n: int) -> int: return math.comb(m-1+n-1, m-1)
组合公式C(n-1, m+n-2)=C(m-1, m+n-2) , 总共走的步长为m+n-2,纵向 必然要走 m-1 步, 横向必然走n-1 步,从里面寻找 (n-1) 或 (m-1) 的组合
class Solution: def uniquePaths(self, m: int, n: int) -> int: M, N = 1, 1 for i in range(2,min(m,n)):N *= i # min(m,n)-1 +1 # m,n --> 3, 7 C(2, 8) --> 8*7 // 2*1 for i in range(m+n-2, m+n-2 - (min(m,n)-1), -1):M *= i return M//N
时间复杂度 O(max(m,n)), 空间复杂度 O(1)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
链接:题目链接题解:
状态转移方程f(n) = f(n-1) + f(n-2)
下一步要到达第n级阶梯有两种方式
- 一是从第n-1阶跨1阶到达,
- ’二是从第n-2阶跨2阶到达,而到达n-1阶和n-2阶的组合数分别为dp(n-1)和dp(n-2),加起来则为到达n阶的结果。
时间复杂度O(n)
dp[i]保存状态,即i个台阶需要多少种方法
emmm,递归超时了!ljclass Solution: def climbStairs(self, n: int) -> int: a = [0, 1] for i in range(n):a[i%2] = a[i%2] + a[(i+1)%2] b = a[(n+1)%2] return b
斐波那契数列
1个台阶1种 2个台阶有2种:[1,1] [2] 3个台阶有3种:[1,1,1] [1,2] [2,1] 4个台阶有5种:[1,1,1,1] [1,1,2] [1,2,1] [2,1,1] [2,2]
// 一个C++做法 class Solution { public: int climbStairs(int n) { int first = 0, res = 1; for (int i = 1; i <= n ; i ++) res += first, first = res - first; return res; } };
给你一个整数数组 nums ,返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。
示例 1:输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
链接:题目链接题解:
# 划分两部分内容,最后第一份含有一个给定元素,那么另一份就不含有这个元素 class Solution: def subsets(self, n : List[int]) -> List[List[int]]: if not n:return [[]] r=self.subsets(n[:-1]) return r+[a+[n[-1]] for a in r]
借用一下图 @liweiwei1419
a结束条件 b。递归作用找出所有子集 c等价条件:当前选择的+之后选择的==取消这一步选择的,重新这一步选择并选另一个元素
- 1.结束开头的if return 当到达一个特定结束条件时候,就认为这个一步步构建的解是符合要求的解了,可以返回并开始回溯操作了,注意也可以是递归到底的结束
- 2.把解存下来或者打印出来的操作:递归以上的部分
- 3.递归的选择,既先递归
- 4.本次推进结束后,如何回溯到上一步:如pop
注意:
1.pop后要不要接一个递归:一般pop前有两此递归则不用,否则要接,不然pop结束后直接返回到上一步递归结束后执行下一个递归语句了
2.可能连续两次递归的结束才代表一轮结束,开始回溯,这个时候pop后就不用加递归了
- 长度超过size后本轮结束
- 下两语句存值
- 递归:作用是每次递归存入一个nums的子集向量(所以i来推进)
- 结束时,pop一个,回到上一步i值相同但没有选2的操作推进
class Solution { void generate(int i,vector
& nums,vector & item, vector >& result){ if(i>=nums.size()) return;//i是从0开始,所以范围0~size-1,即推进结束时候第一次递归结束 item.push_back(nums[i]);//把当前第i元素push result.push_back(item);//把此时的这个向量num,push generate(i+1, nums,item,result );//第一次递归,num往后推进,不断push item.pop_back();//推进结束,pop最后的那个出来 generate(i+1,nums,item,result);//回退到当上一个元素(3)不选时候,进行的第二次递归选择 //详细内部细节: //第二次的i是当前上一步语句的i2,i+1=3,也就是说i=2时候,进行gene(i+1=3)递归,return了,pop后,gene(3),rfeturn,后此刻总的genere(i=2)这个递归结束返回,而这个gene(2),对应的是gene(1)总递归里面的,gene(1+1)这第一次递归,此时返回到这里了然后pop,进行第二次递归。 //i=1,对应于选2和不选2 的情况,回溯就是第一次回退一步选不选3,然后不选3 时候情况,第二次回退到选不选2的那一步,然后执行不选2的那一步计算,会是什么情况,而不选2时候,i自然和选2的时候一样加1了,变成了 i=2,此时item只有1(因为2pop走了)执行gene(2)时候,push(nums(2))也就是3push进此时的item里 item{1,3},情况出现 //最后加不加return都无所谓,因为这个地方就结束了,必定是要返回的 } public: vector > subsets(vector & nums) { vector item={};//item用来承装nums各个情况,初始化为空,然后先放进item中,因为题目要求result里面得有个空向量 vector > result; result.push_back(item);//放进这个空向量 //开始回溯法放入nums generate(0,nums,item,result); return result; } }