作为一名本科计算机科学与技术的学生,《数据结构与算法》《算法分析与设计》这两门课没有学到特别好

上了研究生之后感觉,这些东西需要重新拾起

于是,我找了一个平台,LeetCode,很好的一个刷题的平台,很多大厂的面试题,就出现在上面。

于是我下定决心,我要好好实践好算法,这样才能做一个有人要的打工人,呜呜呜。

(2020/12/19更新:有一些题目没有直接放链接,但是有价值的题目我一定会放在这里,供以后怀念,呜呜呜)

chapter1:递归

  面试题 16.11. 跳水板

  938. 二叉搜索树的范围和

  783. 二叉搜索树节点最小距离

  98. 验证二叉搜索树

  (lookdown563. 二叉树的坡度(特别需要注意在Python中global与nonlocal之间的区别,复习一下留下一个问题:是否是后序遍历?),

  (lookdown283. 移动零

  (lookdown242. 有效的字母异位词(一道简单题可以学到很多Algorithm Wisdom),

  (lookdown222. 完全二叉树的节点个数(用递归和非递归的差别),

  (lookdown面试题 08.05. 递归乘法(极其有趣的一道题),

  (lookdown剑指 Offer 16. 数值的整数次方(一开始就觉得暴力吧,想起来刚学过的高等算法分析与实现老师说过霍纳法则,其实中国更早,叫做秦九韶算法,计算多项式,但是如果只计算第一项an*x^n,该公式退化成暴力法,因此提出了二进制幂乘法问题,矩阵n次幂也是如此。还好自己想起来了n次幂这种求法),

  面试题 08.06. 汉诺塔问题(如何用stack模拟实现),

  698. 划分为k个相等的子集(分而治之法是非常典型的递归,同时回溯也用递归的方法解决),

  (lookdown204. 计数质数(非常简单的“简单题”,太sao了这道题,真的需要点数学基础才能行),

  (lookdown17. 电话号码的字母组合(真开心自己做出来了,经典的回溯代码),

  (lookdown1306. 跳跃游戏 III(接着上面的回溯接着写代码),

  (lookdown779. 第K个语法符号(这是一道值得反思的“回溯题”,非常值得和1306和前面的1306和17对比),

  118. 杨辉三角

  90. 子集 II 幂集的产生,比如求[13,2,2,1,2]的幂集

  77. 组合 (组合问题。(不是我总结的)递归的模板放在下面)

chapter2:数据结构基本结构-栈stack

  剑指 Offer 09. 用两个栈实现队列

  (lookdown)剑指 Offer 30. 包含min函数的栈(犯了一个小错误),

  20. 有效的括号(stack,简单题,但是要注意一些简单的坑)

  62. 不同路径

  860. 柠檬水找零(暂且用暴力的方法进行模拟,其实可以考虑用回溯的方法!!!其实也可以记录$5,$10,¥20的个数(只有三种纸币,简单),这样take change 特别方便)

1021. 删除最外层的括号(用了笨方法,肯定还可以优化,晚上22:44了,先回宿舍了!!!)

  1047. 删除字符串中的所有相邻重复项(用stack极其简单,特别是python语言)

  面试题 03.02. 栈的最小值(对上面的题目稍稍改造了一点点,解释之前的题目剑指 Offer 30. 包含min函数的栈)

  155. 最小栈(与面试题 03.02. 栈的最小值一模一样,the same way to solve it)

  1544. 整理字符串

  682. 棒球比赛(这道题引出一个问题,Python如何确定你输入的string是数字,本题的代码我采用了避开这个问题,但是若要是避免不了,how can I do?)

  389. 找不同(很多种方法可以解题,但是值得学的是一种很奇妙的  Python 位运算 (用来找不同)方法,太奇妙了,因为位运算针对于找“单个值”真的太方便了)

  1441. 用栈操作构建数组

  面试题 03.04. 化栈为队 (学会比较化栈为队,和化队为栈,思路简直一模一样)

  1598. 文件夹操作日志搜集器(没有使用栈的解法)

  225. 用队列实现栈 (学会比较化栈为队,和化队为栈,思路简直一模一样)

  173. 二叉搜索树迭代器(非常值得学的一道题,not very ,it's very more,巧妙的 栈+受控递归(有大佬说这是一种《算法导论》中的摊还分析Amortized Analysis))

  35. 搜索插入位置(非常“简单”的简单题,自己要总结一些二分法的坑,该题属于“二分法”)

  103. 二叉树的锯齿形层序遍历(层次遍历,该次属于“队列”,将每一层分层次存储,用两个指针即可)

  剑指 Offer 59 - II. 队列的最大值(好好理解双端队列的基本操作,以及该题是怎么维护单调队列,这一种数据结构的)

  1381. 设计一个支持增量操作的栈(1.模拟 2.模拟+改进)

  205. 同构字符串(绝对够学的一道同构题,与之前290. 单词规律很像,主要是要想好怎么给“同构”下定义)

  144. 二叉树的前序遍历(递归与迭代,迭代法好像需要O(n)的空间复杂度,因为需要用到栈stack,但是官方给出了一种O(1)的解法,居然可以将空间复杂度降到最低,看一下总还是可以的,菜鸟的我也只能也只能看一下这么高级的算法)

  496. 下一个更大元素 I (思考用hashmap的原因和好处?栈少有的应用:单调栈,怎么维护一个单调栈,这才是关键?)

  739. 每日温度(这一题与上一题496. 下一个更大元素 I 相比有差别,差别是已知数据中温度中可能出现重复值(hashmap不允许出现重复值)因此我们怎么办呢?办法:因为题目要求的是后几个元素,因为stack就存数组的下标就行,不再是是像496. 下一个更大元素 I 一样,存入数组中的数值,应该好好体会一下这两题之间的区别)

  605. 种花问题(别小看这道真的很简单的简单题,第二个简单题没有打双引号,但是真的很奇怪,解法1:前后补充种花(怎么补充?)解法2:贪心,怎么贪,能种就种,管他的,贪心就是目前情况能怎么样就怎么样,不管以后怎么样)  

  (持续更新)

chapter3:动态规划dp

  首先动态规划的步骤(动态规划五部曲)是:

  (0.举例推导dp数组,发现第一步dp数组规律的过程就是举例推到dp数组的过程)

    1.确定dp数组(dp table)以及下标的含义 ,dp数组是一维还是二维还是三维?(持续更新)

    2.确定递推公式

    3.dp数组怎么初始化

    4.确定遍历顺序

    5.举例推导dp数组

  动态规划怎么debug???(灵魂三问)

    1.这道题目我举例推导过转移公式吗?

    2.我打印dp数组日志了吗?

    3.打印出来的dp数组日志是和我想的一样吗?

    振聋发聩,专治DP疑难杂症,题目刷起来!!!wuuuu

  斐波那契数列是最简单的动态规划问题

  剑指 Offer 42. 连续子数组的最大和

  面试题 17.16. 按摩师

  面试题 08.01. 三步问题

  70. 爬楼梯

  746. 使用最小花费爬楼梯(爬楼梯的稍微改变版)

  303. 区域和检索 - 数组不可变 简单题中体会 “前缀和”的使用

  392. 判断子序列 (really?一切即可...dp???huhhhhh,其实双指针效率挺好的,终于AC了)

  62. 不同路径

  63. 不同路径 II

  343. 整数拆分(第一次接触到这种类型的动态规划,有点矩阵连乘的感觉,特意回去看了看本科Sunny老师【此处感谢Sunny老师,没有他的耐心细心教导,我觉得我找不到计算机的兴趣】的“矩阵连乘”的算法(我居然想的起来2-3年前的知识,感觉好神奇呀!!!),学会比较,在比较中学习,理解的知识面将会以“指数爆炸式”增长,yets显然我tai vegetable了)

  132. 分割回文串 II(困难题,但是非常值得学习其中的方法与细节)

  背包系列之0-1背包

  基础知识:0-1背包初讲,用二维dp数组dp[][];然后对0-1背包进行空间优化,用dp数组dp[],这个非常重要,为什么用一位数组的时候需要倒序,因为正序会出现覆盖问题;接下来便是学会将0-1背包问题转换问题。

  所以,0-1背包的实质就是从一堆“物品”中挑选一些“物品”放进背包,使得背包内的“价值”(“价值”是目标)最大?

  416. 分割等和子集 (0-1背包问题转化问题:是否可以从数组中选择出一些数字,重点理解dp[i] = i?这个判定条件)

  1049. 最后一块石头的重量 II (0-1背包问题转化问题:是否可以从一堆数字当中,dp[i] 的含义和0-1背包的含义很相近)

  494. 目标和 (另外一种0-1背包转化问题,不要将第一个物品放入初始化)

  474. 一和零 (之前0-1背包的维度(也就是目标的维度)都是一维,比如dp[j]表示剩余空间为j时最大能够达到的价值(目标:价值),或者dp[j]表示空间为j时能够满足的方案数为dp[j](目标:方案数)等,但是该题变成了二维,值得学习)

  70. 爬楼梯  动态规划:以前我没得选,现在我选择再爬一次!(摘自Carl哥)用动态规划的思维去想爬楼梯问题

  322. 零钱兑换  动态规划: 给我个机会,我再兑换一次零钱 (摘自Carl哥)用动态规划再次兑换零钱

  279. 完全平方数 和上题322. 零钱兑换 一模一样,学会了思想,接下来就是遍历循序的问题了

  139. 单词拆分 完全背包的转化,遍历顺序最好是先背包,再物品,为什么?需要好好体会!注意Python中s[1:3]的取值范围是下标为1,2,不包括3

  0-1背包问题暂且完毕,接下来便是年后的动态规划了,2021年后dp

  198. 打家劫舍 再次理解dp[i]表示包括i的前i个房间能够达到的最大金额,取决与这个第i个房间的偷与不偷,不偷的话,就是dp[i-1](包括i-1的前i-1个房间能够达到的最大金额),偷的话就是dp[i-2] + nums[i](包括i-2的前i-2个房间能够达到的最大金额加上第i个房间的金额),dp[i] = max(dp[i-1], dp[i-2]+nums[i]),初始化应该是dp[2] = max(dp[1], dp[0]+nums[0]) (i取2,粗体为初始化对象),遍历顺序显然是从前往后

  213. 打家劫舍 II 在上题198. 打家劫舍的基础上怎么处理成环问题?【分解环】

  337. 打家劫舍 III  解法一:在树上执行198. 打家劫舍,入门树状dp  ; 解法二:DFS后序遍历+memoryMap,简称记忆化深搜 这道题值得复习了

  动态规划问题之股票问题

  121. 买卖股票的最佳时机 不容易想到的动态规划问题,需要好好看几遍,看看代码中的详细注解,另外还有一种想法:贪心

  心想(贪的角度):要是我能够在最小值(尽管最小值可能出现在数组最后一位)的时候买入,之后只要比最小值大就可以了

  309. 最佳买卖股票时机含冷冻期 写了很多注释,值得一看,重视思路的培养 

  583. 两个字符串的删除操作

  674. 最长连续递增序列 VS  300. 最长递增子序列 比较连续与否对递推关系式的影响

  (持续更新)

chapter4:回溯(跟着Carl哥刷的回溯)

  216. 组合总和 III  组合问题

  77. 组合 (组合问题。(不是我总结的)递归的模板放在下面,见chapter1:递归)

  131. 分割回文串 看代码处有写明错误之处,一定得看一下教训

chapter5:贪心(跟着Carl哥刷的贪心)

  贪心没有捷径(贪心是朴素的思路,所以经常让我们忘记这一重要的算法)、思考点是可不可以找到一个贪的点,能够让局部最优到达全局最优。

  455. 分发饼干  与排序贪心,贪的角度:为了不浪费饼干,让饼干最大的给胃口最大的。

  376. 摆动序列  没有排序,贪的角度:寻找下一个满足条件的点(保持区间波动,只需要把单调区间上的元素移除就可以了。

  20200904

  45. 跳跃游戏 II  不要一直想当前应该走几步,而是当前我能够走到哪里的范围!

chapterX:哈希表

  剑指 Offer 50. 第一个只出现一次的字符 (用数组做哈希,比set,map这样的数据结构快一点,学会用数组用哈希)

  (持续更新)

chapterX:拓扑排序(该类型的题目在leetcode上目前发现题量较少)

  207. 课程表(第一次写拓扑排序,感觉用Brute Force的办法可能效率较低;一般情况下是已知有向无环图,然后给出拓扑排序的顺序,该题是给出一个图,要你去判断存不存在拓扑排序。关键是怎么判断存不存在拓扑排序的条件)

  210. 课程表 II (与207. 课程表一模一样,这是对最简单的拓扑排序的加强版,若是存在多种拓扑排序,只需要输出任意一种合理的方式即可)

  (持续更新)

chapterX:双指针(别小看这没人注意的双指针,多用来解决字符串问题,且效率极高)

  830. 较大分组的位置

  392. 判断子序列 (really?一切即可...dp???huhhhhh,其实双指针效率挺好的)

  27. 移除元素 复习双指针做的题,双指针常用于数组处理和链表处理,且是一种十分高效的方法 【快慢双指针法】

  (持续更新)

chapterX:待分组

  1232. 缀点成线    这题目虽然简单,但是可以从中学到其他东西,(期末考试)等待更新

1. 两数之和 这题注意看代码,为什么不能两句代码的顺序?

chapter1 Resolution:递归

563.二叉树的坡度 的题解,感觉非常奇妙,特别是最后一句“而左子树之和加右子树之和加节点值为总树的和,巧妙复用了”。“复用”一词用的非常准确,在这里感谢这位大佬分享了代码。不知道怎么应用大佬的代码,直接截图过来了,反正除了我应该也没人看。

283.移动零 的题解,话说是简单题(但是要求不允许开数组,同时尽量减少比较次数),但其实还是有很多够我这个菜鸟学的。这里总结一下双指针的解法。

双指针:1.i指针当然是遍历整个数组的指针,它的关注点发现非零元素。2.j指针指示所有非零元素的位置。

解法:i指针从头扫到尾,发现一个非零的数,就把这个数与j指针指向的数覆盖,然后j指针就开始后移,表明在j前面的元素都是非零元素。这样i指针一遍扫下来,就把所有的非零元素全部按顺序移到了前面。i扫完后,此时的j指针就指向了所有非零元素的后一个空位,如图所示。接下来便是把j指针指向的后面所有元素改成0即可完成操作。

 1 class Solution:
2 def moveZeroes(self, nums: List[int]) -> None:
3 """
4 Do not return anything, modify nums in-place instead.
5 """
6 res_end = 0
7 nums_count = len(nums)
8 for i in range(nums_count):
9 if nums[i] != 0:
10 nums[res_end] = nums[i]
11 res_end += 1
12
13 for i in range(res_end,nums_count):
14 nums[i] = 0
15 # nums.sort(key=lambda x:x==0)

移动零

242.有效的字母异位词

这道题三道解法,1.暴力 violence 2.Python骚操作 3.哈希

由图可知,哈希时间空间复杂度都是很好的,随便学习了一种新的方法,哈希

 1 class Solution:
2 def isAnagram(self, s: str, t: str) -> bool:
3 # 解法一:暴力法 n方
4 # A = []
5 # B = []
6 # for i in range(len(s)):
7 # A.append(s[i])
8 # for i in range(len(t)):
9 # B.append(t[i])
10 # if len(s) == len(t):
11 # for i in A:
12 # if i in B:
13 # B.remove(i)
14 # else:
15 # return False
16 # return True
17 # return False
18
19 # 解法二:排序法
20 # A = sorted(s)
21 # B = sorted(t)
22 # return A == B # [1,2]=[2,1]这是成立的
23
24 # 解法三:哈希
25 arr = [0] * 26
26 for i in range(len(s)):
27 arr[ord(s[i])-97] += 1 # ord("a")=97 ord函数表示将字符转成ASCII chr()将ASCII转成字母
28
29 for i in range(len(t)):
30 arr[ord(t[i])-97] -= 1
31
32 for i in arr:
33 if i == 0:
34 pass
35 else:
36 return False
37 return True

有效的字母异位词

222. 完全二叉树的节点个数 递归和非递归的时间和空间差别,最开始用队列模拟的方法时间和空间都比较大,但是用递归时稍微少一点,但是空间差不多,我隐隐约约感觉还有更好的方法,毕竟是完全二叉树,可以利用完全二叉树的特性。

 1 # Definition for a binary tree node.
2 # class TreeNode:
3 # def __init__(self, x):
4 # self.val = x
5 # self.left = None
6 # self.right = None
7
8 class Solution:
9 def countNodes(self, root: TreeNode) -> int:
10 # # 采用层次遍历,进行计数
11 # if not root:
12 # return 0
13 # Queue = []
14 # Queue.append(root)
15
16 # s = 0
17 # while(Queue):
18 # x = Queue.pop(0)
19 # s += 1
20 # if x.left:
21 # Queue.append(x.left)
22 # if x.right:
23 # Queue.append(x.right)
24
25 # return s
26
27 # 递归
28 if not root:
29 return 0
30 s= 1 # 初始化根节点计数为1
31 def reverse(root):
32 nonlocal s
33 if not root:
34 return 0;
35 if root.left:
36 reverse(root.left)
37 s += 1
38 if root.right:
39 reverse(root.right)
40 s += 1
41
42 reverse(root)
43
44 return s

完全二叉树的节点个数

面试题 08.05. 递归乘法(极其有趣的一道题)递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。最开始的想法就是将乘法转移成加法,看了别人的思路之后,马上想起来刚学过的“俄式乘法”(对不起刚学过得算法老师,刚学不久就忘了,o(╥﹏╥)o)

用了两种方法解题,一直以为快速幂应该会快一点,但是运行完之后发现时间居然还慢一点(最简单的思路花时间36ms,但是没想到快速幂居然花了40ms,eeeeeee.....)

 1 class Solution:
2 def multiply(self, A: int, B: int) -> int:
3 if A == 0 and B == 0:
4 return 0
5
6 # A, B = min(A, B), max(A, B)
7 if A == 1:
8 return B
9 if A & 1 == 1:
10 t = int(self.multiply((A-1)>>1, B)<<1) + B
11 else:
12 t = int(self.multiply((A)>>1, B)<<1)
13
14 return t
15
16 # 简单的将乘法转换成加法来做
17 # if A > B:
18 # # 交换AB
19 # t = A
20 # A = B
21 # B = t
22 # s = 0
23
24 # def nomulti(A, B):
25 # nonlocal s
26 # if A == 0:
27 # return ;
28 # s = s + B
29 # nomulti(A-1, B)
30
31 # nomulti(A,B)
32 # return s

递归乘法

剑指 Offer 16. 数值的整数次方 代码如下:

 1 class Solution:
2 def myPow(self, x: float, n: int) -> float:
3 if n == 0:
4 return 1;
5 elif n > 0:
6 y = 1
7 while n:
8 if n & 1:
9 y = y * x
10 x = x * x
11 n = n >> 1
12 return y
13 elif n < 0:
14 y = 1
15 n = -n
16 while n:
17 if n & 1:
18 y = y * x
19 x = x * x
20 n = n >> 1
21 return 1/y

数值的整数次方

204. 计数质数 这道暴力解绝对不行,就算我用python埃筛了一遍也不行,还要继续优化,愁了一天,哎!!!(注释的是第一种方法,超时,后面对其进行改进才可以过)

 1 class Solution:
2 def countPrimes(self, n: int) -> int:
3
4 # def isPrime(i):
5 # for ind in range(2, int(sqrt(i))+1):
6 # if i % ind == 0:
7 # return False
8 # return True
9
10 # if n == 0 or n == 1:
11 # return 0
12 # A = [i for i in range(2,n)]
13
14 # candidate_prime = []
15 # # 候选质数
16 # for i in range(2, int(sqrt(n))+1):
17 # if isPrime(i):
18 # candidate_prime.append(i)
19 # # print(candidate_prime)
20
21 # #循环消除
22 # for i in range(len(candidate_prime)):
23 # j = candidate_prime[i] * candidate_prime[i]
24 # while j < n:
25 # if j in A:
26 # A.remove(j)
27 # j += candidate_prime[i]
28 # return len(A)
29
30 def isPrime(i):
31 for ind in range(2, int(sqrt(i))+1):
32 if i % ind == 0:
33 return False
34 return True
35
36 if n == 0 or n == 1:
37 return 0
38 A = [i for i in range(2, n)]
39
40 for i in range(2, int(sqrt(n))+1):
41 if A[i-2] and isPrime(i):
42 x = i * i
43 while x < n:
44 A[x-2] = 0
45 x = x + i
46 ans = 0
47 for i in A:
48 if i != 0:
49 ans += 1
50 return ans

计数质数

17. 电话号码的字母组合 回溯&递归,顺便补充一个,将“abc”字符串直接转成['a', 'b', 'c']的方法。

 1 class Solution:
2 def letterCombinations(self, digits: str) -> List[str]:
3 if digits == "":
4 return []
5 num_letter = [
6 ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['j', 'k', 'l'], ['m', 'n', 'o'],
7 ['p', 'q', 'r', 's'], ['t', 'u', 'v'], ['w', 'x', 'y', 'z']
8 ]
9 # digits = sorted(digits)
10 digits = list(digits)
11 length = len(digits)
12
13 dig = []
14 for i in range(len(digits)):
15 dig.append(int(digits[i]) - 2)
16 # print("dig", dig)
17
18 res = []
19
20 # nums = [0, 1]
21 def backtrace(nums, k, s):
22 if k == length:
23 res.append(str(s))
24 return ;
25 x = nums[k]
26 for i in range(len(num_letter[x])):
27 s += str(num_letter[x][i])
28 backtrace(nums,k+1,s)
29 s = s[:-1]
30
31 backtrace(dig, 0, '')
32
33 return res

电话号码的字母组合

1306. 跳跃游戏 III 回溯& 递归 加vis数组稍微优化一下

 1 class Solution:
2 def canReach(self, arr: List[int], start: int) -> bool:
3 if not arr:
4 return False;
5
6 length = len(arr)
7 # vis(ited) 数组稍微优化一下
8 vis = [0] * length
9 if start > length:
10 return False
11
12 def backtrace(index, arr):
13 if arr[index] == 0:
14 return True
15 else:
16 if 0 <= index + arr[index] <= length - 1 and vis[index + arr[index]] == 0:
17 vis[index + arr[index]] = 1
18 if backtrace(index + arr[index], arr):
19 return True
20 if 0 <= index - arr[index] <= length - 1 and vis[index - arr[index]] == 0:
21 vis[index - arr[index]] = 1
22 if backtrace(index - arr[index], arr):
23 return True
24 return False
25
26 TF = backtrace(start, arr)
27 vis[start] = 1
28
29 return TF
30

跳跃游戏 III

779. 第K个语法符号 一开始我就直接写了没有剪枝的回溯法,结果在N=30会超时。所以重新画了图,观察规律,其实这是二叉树。(红色部分为该层的编号,标有*号的编号3和4分别是其父节点编号2的两倍和两倍-1)

为什么要用这个规律呢?因为其实我们知道题目已知的K(第N层中的)之后我们就可以推算K-1层中的编号,以此类推,比如我们假设我们知道1(2),我们可以按照那个两倍规律,推算出1(2)-->0(1)-->0(1)-->0(1)。

 1 class Solution:
2 def kthGrammar(self, N: int, K: int) -> int:
3
4 if K > pow(2, N-1):
5 return ;
6
7 # def backtrace(t, arr):
8 # if t == N:
9 # return arr[K-1]
10 # tmp = []
11 # for i in range(len(arr)):
12 # if arr[i] == 0:
13 # tmp.append(0)
14 # tmp.append(1)
15 # elif arr[i] == 1:
16 # tmp.append(1)
17 # tmp.append(0)
18 # arr = tmp
19 # return backtrace(t+1, arr)
20
21 # T = backtrace(1, [0])
22
23 # return T
24
25 feature_list = []
26 feature_list.append(K)
27 while N-1:
28 K = ceil(K / 2)
29 feature_list.append(K)
30 N -= 1
31
32 feature_list = sorted(feature_list)
33 print(feature_list)
34
35 t = 0
36 for i in range(1,len(feature_list)):
37 if feature_list[i] == feature_list[i-1] * 2 and t == 0:
38 t = 1
39 elif feature_list[i] + 1 == feature_list[i-1] * 2 and t == 0:
40 t = 0
41 elif feature_list[i] == feature_list[i-1] * 2 and t == 1:
42 t = 0
43 elif feature_list[i] + 1 == feature_list[i-1] * 2 and t == 1:
44 t = 1
45 # print(t)
46
47 return t

第K个语法符号

77. 组合   终于,递归的模板出来了,回溯是典型要使用递归的(模板取自《代码随想录》,方便以后复习的时候用)。

 1 class Solution:
2 def combine(self, n: int, k: int) -> List[List[int]]:
3
4
5 A = []
6
7 def backtrace(k,start,end,arr):
8 nonlocal A
9 if k == 0:
10 # 简单赋值与浅拷贝、深拷贝之间的关系
11 # 将arr内元素拷贝到T中
12 T = []
13 for ind in range(len(arr)):
14 T.append(arr[ind])
15 A.append(T)
16 return;
17
18 for i in range(start, end+1):
19 arr.append(i)
20 k -= 1
21 backtrace(k,i+1,end,arr)
22 k += 1
23 arr.pop()
24
25 backtrace(k,1,n,[])
26
27 # print(A)
28 return A

组合

chapter2 Resolution:数据结构基本结构-栈stack

62. 不同路径 dfs runing out of time ,仔细一想这不是个数学问题嘛!!!

 1 class Solution:
2 def uniquePaths(self, m: int, n: int) -> int:
3 # dfs 会 running out of time
4 # path = 0
5
6 # def backtrace(row, col):
7 # nonlocal path
8 # if row > m or col > n:
9 # return ;
10 # if row == m and col == n:
11 # path += 1
12 # return ;
13 # backtrace(row+1, col)
14 # backtrace(row, col+1)
15
16 # backtrace(1,1)
17 # return path
18
19
20 down = 1 # 分母
21 up = 1 # 分子
22 A = m+n-2
23
24 for i in range(m-1):
25 down *= A
26 A -= 1
27
28 for i in range(1,(m-1)+1):
29 up *= i
30
31 return int(down/up)

不同路径

 1 class Solution:
2 def uniquePaths(self, m: int, n: int) -> int:
3 # 动态规划方法
4 # dp[i][j] 到达第i行和第j列的方案数量,有多少种走法
5 dp = [[1] * m] * n
6
7 # 初始化,dp[:][0] = 0 and dp[0][:] = 1
8
9 for i in range(1,n):
10 for j in range(1,m):
11 dp[i][j] = dp[i][j-1] + dp[i-1][j]
12
13 # print(dp)
14 return dp[n-1][m-1]

不同路径 更新(2021/1/11)

剑指 Offer 30. 包含min函数的栈 借助于helpStack进行求最小值的优化(其基本思想就是保存当下stack中的最小值,就这么简单的思想),helpStack的特点是自栈底到栈顶维持非递增结构,如[2,3,4,4,5,9]

 1 class MinStack:
2
3 # def __init__(self):
4 # """
5 # initialize your data structure here.
6 # """
7 # self.Stack = []
8
9 # def push(self, x: int) -> None:
10 # self.Stack.append(x)
11
12 # def pop(self) -> None:
13 # self.Stack.pop()
14
15 # def top(self) -> int:
16 # return self.Stack[-1]
17
18 # def min(self) -> int:
19 # return min(self.Stack)
20
21 def __init__(self):
22 """
23 initialize your data structure here.
24 """
25 self.Stack = []
26 self.helpStack = []
27
28 def push(self, x: int) -> None:
29 self.Stack.append(x)
30 if not self.helpStack:
31 self.helpStack.append(x)
32 elif self.helpStack and x <= self.helpStack[-1]: # 一开始的时候该句写成了if self.helpStack and x <= self.helpStack[-1]导致出错
33 self.helpStack.append(x)
34
35 def pop(self) -> None:
36 x = self.Stack.pop()
37 if x == self.helpStack[-1]:
38 self.helpStack.pop()
39
40 def top(self) -> int:
41 return self.Stack[-1]
42
43 def min(self) -> int:
44 return self.helpStack[-1]
45
46 # Your MinStack object will be instantiated and called as such:
47 # obj = MinStack()
48 # obj.push(x)
49 # obj.pop()
50 # param_3 = obj.top()
51 # param_4 = obj.min()

包含min函数的栈

860. 柠檬水找零

 1 class Solution:
2 def lemonadeChange(self, bills: List[int]) -> bool:
3 s = []
4 cnt_5_dollars = 0
5 for i in range(len(bills)):
6 if bills[i] == 5:
7 s.append(5)
8 cnt_5_dollars += 1
9 elif bills[i] == 10:
10 if 5 in s:
11 s.remove(5)
12 cnt_5_dollars -= 1
13 s.append(10)
14 else:
15 return False
16 elif bills[i] == 20:
17 if 5 in s and 10 in s:
18 s.remove(5)
19 cnt_5_dollars -= 1
20 s.remove(10)
21 s.append(20)
22 continue
23
24 if cnt_5_dollars >= 3:
25 print(cnt_5_dollars)
26 s.remove(5)
27 s.remove(5)
28 s.remove(5)
29 cnt_5_dollars -= 3
30 s.append(20)
31 continue
32 return False
33
34 return True

柠檬水找零

1021. 删除最外层的括号 第一种方法是记录要删除的位置,第二种方法就是之前学到的双指针问题(见283. 移动零)。使用双指针速度大大加快!

 1 class Solution:
2 def removeOuterParentheses(self, S: str) -> str:
3 if S == "":
4 return ""
5 # 记录删除符号的位置,然后集中删除
6 # stack = []
7 # arr = list(S)
8
9 # remove_loc = []
10 # for i in range(len(arr)):
11 # if arr[i] == "(":
12 # stack.append(arr[i])
13 # if len(stack) == 1:
14 # remove_loc.append(i)
15 # if arr[i] == ")":
16 # if len(stack) != 1:
17 # x = stack.pop()
18 # elif len(stack) == 1:
19 # remove_loc.append(i)
20 # stack.pop()
21
22 # ans = ""
23 # # print(remove_loc)
24 # for i in range(len(arr)):
25 # if i not in remove_loc:
26 # ans += arr[i]
27
28 # return ans
29
30 # 双指针法
31 arr = list(S)
32 L = len(arr)
33 stack = []
34
35 point = 0
36 for i in range(L):
37 if arr[i] == "(":
38 stack.append(arr[i])
39 if len(stack) >= 2:
40 arr[point] = arr[i]
41 point += 1
42 elif arr[i] == ")":
43 x = stack.pop()
44 if len(stack) != 0:
45 arr[point] = arr[i]
46 point += 1
47 # print(arr[:point])
48
49 return "".join(arr[:point])
50

删除最外层的括号

682. 棒球比赛

 1 class Solution:
2 def calPoints(self, ops: List[str]) -> int:
3 # 栈stack模拟
4 stack = []
5 for i in range(len(ops)):
6 if ops[i] == "C":
7 stack.pop()
8 elif ops[i] == "D":
9 stack.append(stack[-1] * 2)
10 elif ops[i] == "+":
11 stack.append(stack[-1]+stack[-2])
12 else:
13 stack.append(int(ops[i]))
14
15 return sum(stack)

棒球比赛

389. 找不同 哈希计数,位运算值得考虑。

 1 class Solution:
2 def findTheDifference(self, s: str, t: str) -> str:
3 # 可以考虑排序、可以考虑计数(哈希)
4 # A = sorted(s)
5 # B = sorted(t)
6
7 # for i in range(len(A)):
8 # if A[i] == B[i]:
9 # pass
10 # else:
11 # return B[i]
12 # return B[-1]
13
14 # 还可以考虑位运算 异或 ,这种方法在特定情形下太巧秒了
15 res = 0
16
17 s = s + t
18 res = 0
19 for i in range(len(s)):
20 res = res ^ ord(s[i])
21 # print(res)
22 return chr(res)

找不同

面试题 03.04. 化栈为队

 1 class MyQueue:
2
3 def __init__(self):
4 """
5 Initialize your data structure here.
6 """
7 self.stack = []
8 self.helper_stack = []
9
10
11 def push(self, x: int) -> None:
12 """
13 Push element x to the back of queue.
14 """
15 self.stack.append(x)
16
17 def pop(self) -> int:
18 """
19 Removes the element from in front of queue and returns that element.
20 """
21 if self.helper_stack:
22 return self.helper_stack.pop()
23 elif not self.helper_stack and not self.stack:
24 return -1
25 elif not self.helper_stack and self.stack:
26 for i in range(len(self.stack)):
27 self.helper_stack.append(self.stack.pop())
28 return self.helper_stack.pop()
29
30
31 def peek(self) -> int:
32 """
33 Get the front element.
34 """
35 if self.helper_stack:
36 return self.helper_stack[-1]
37 elif not self.helper_stack and not self.stack:
38 return -1
39 elif not self.helper_stack and self.stack:
40 for i in range(len(self.stack)):
41 self.helper_stack.append(self.stack.pop())
42 return self.helper_stack[-1]
43
44
45 def empty(self) -> bool:
46 """
47 Returns whether the queue is empty.
48 """
49 if not self.helper_stack and not self.stack:
50 return True
51 else:
52 return False
53
54
55
56 # Your MyQueue object will be instantiated and called as such:
57 # obj = MyQueue()
58 # obj.push(x)
59 # param_2 = obj.pop()
60 # param_3 = obj.peek()
61 # param_4 = obj.empty()

化栈为队

173. 二叉搜索树迭代器

 1 # Definition for a binary tree node.
2 # class TreeNode:
3 # def __init__(self, val=0, left=None, right=None):
4 # self.val = val
5 # self.left = left
6 # self.right = right
7 class BSTIterator:
8
9 def __init__(self, root: TreeNode):
10 # 方法二:
11 self.stack = []
12 # 初始化的时候找到第一个next的位置,就是中序遍历的第一个节点
13 self.search_left(root)
14
15 def search_left(self, root):
16 while root:
17 self.stack.append(root)
18 root = root.left
19
20 def next(self) -> int:
21 # 方法二:
22 node = self.stack.pop()
23
24 # 步骤:将stack栈顶元素出栈,然后寻找中序遍历下一个元素进栈
25 if node.right:
26 self.search_left(node.right)
27 return node.val
28
29
30
31 def hasNext(self) -> bool:
32
33 # 方法二:
34 if self.stack:
35 return True
36 else:
37 return False
38
39 # class BSTIterator:
40 # def __init__(self, root: TreeNode):
41
42 # # 方法一:是最容易想到的方法
43 # self.stack = []
44
45 # def fun(root):
46 # if not root:
47 # return ;
48 # fun(root.left)
49 # self.stack.append(root.val)
50 # fun(root.right)
51 # fun(root)
52
53 # def next(self) -> int:
54 # # 方法一:
55 # return self.stack.pop(0)
56
57 # def hasNext(self) -> bool:
58 # # 方法一:
59 # if self.stack:
60 # return True
61 # else:
62 # return False
63
64 # Your BSTIterator object will be instantiated and called as such:
65 # obj = BSTIterator(root)
66 # param_1 = obj.next()
67 # param_2 = obj.hasNext()

二叉搜搜树迭代器

35. 搜索插入位置

 1 class Solution:
2 def searchInsert(self, nums: List[int], target: int) -> int:
3
4 # O(n)
5 # for i in range(len(nums)):
6 # if nums[i] >= target:
7 # return i
8
9 # return len(nums)
10
11 # O(logn)
12 left, right = 0, len(nums) - 1
13
14 if nums[left] > target:
15 return 0
16 if nums[right] < target:
17 return right + 1
18 while left <= right:
19
20 mid = int((right + left)>>1)
21 if right - left == 1:
22 if nums[left] == target:
23 return left
24 elif nums[right] == target:
25 return right
26 else:
27 return right
28 break
29
30 if nums[mid] < target:
31 left = mid
32 elif nums[mid] > target:
33 right = mid
34 elif nums[mid] == target:
35 return mid

搜索插入排序

103. 二叉树的锯齿形层序遍历

 1 # Definition for a binary tree node.
2 # class TreeNode:
3 # def __init__(self, x):
4 # self.val = x
5 # self.left = None
6 # self.right = None
7
8 class Solution:
9 def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
10
11 # 特殊情况
12 if not root:
13 return []
14 queue = []
15 queue.append(root)
16 p = 1
17 T = 1 # Turn 等于表示正插,-1表示反插
18
19 ans = []
20 while queue:
21 arr = []
22 m = 0
23
24 while p != 0:
25 x = queue.pop(0)
26 if T == 1:
27 arr.append(x.val)
28 else:
29 arr.insert(0, x.val)
30 p -= 1
31
32 if x.left:
33 queue.append(x.left)
34 m += 1
35 if x.right:
36 queue.append(x.right)
37 m += 1
38 p = m
39 T = -T
40
41 ans.append(arr)
42
43 # print(ans)
44 return ans
45
46
47

二叉树的锯齿形层序遍历

剑指 Offer 59 - II. 队列的最大值

 1 class MaxQueue:
2
3 def __init__(self):
4 self.queue = []
5 self.deque = deque([])
6
7 def max_value(self) -> int:
8 if not self.deque:
9 return -1
10 return self.deque[0]
11
12 def push_back(self, value: int) -> None:
13 self.queue.append(value)
14 if not deque:
15 self.deque.append(value)
16 else:
17 while self.deque:
18 if self.deque[-1] < value:
19 self.deque.pop()
20 else:
21 break
22 self.deque.append(value)
23
24
25 def pop_front(self) -> int:
26 if self.queue:
27 t = self.queue.pop(0)
28 if self.deque[0] == t:
29 self.deque.popleft()
30 return t
31 else:
32 return -1
33
34 # Your MaxQueue object will be instantiated and called as such:
35 # obj = MaxQueue()
36 # param_1 = obj.max_value()
37 # obj.push_back(value)
38 # param_3 = obj.pop_front()

队列的最大值(分摊原则)

205. 同构字符串(哈希表在Python中的数据结构实现:字典dict={})

 1 class Solution:
2 def isIsomorphic(self, s: str, t: str) -> bool:
3
4 map = {}
5 # 同构的定义:双射,如“ab”和“aa”,首先会建立a与a的映射,然后扫描到b和a时,虽然b没有出现,但是a已经出现了,这就不是双射了。
6 for i in range(len(s)):
7 if s[i] not in map.keys():
8 if t[i] not in map.values():
9 map[s[i]] = t[i]
10 else:
11 return False
12 else:
13 if map[s[i]] == t[i]:
14 pass
15 else:
16 return False
17
18 return True

同构字符串

144. 二叉树的前序遍历

 1 # Definition for a binary tree node.
2 # class TreeNode:
3 # def __init__(self, val=0, left=None, right=None):
4 # self.val = val
5 # self.left = left
6 # self.right = right
7 class Solution:
8 def preorderTraversal(self, root: TreeNode) -> List[int]:
9
10 # 借助stack的迭代法,空间复杂读度为O(n)
11 if not root:
12 return []
13 stack = []
14 stack.append(root)
15
16 ans = []
17 while stack:
18 x = stack.pop()
19 ans.append(x.val)
20 if x.right:
21 stack.append(x.right)
22 if x.left:
23 stack.append(x.left)
24
25 return ans
26
27

二叉树的前序遍历

496. 下一个更大元素 I

 1 class Solution:
2 def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
3 # hashmap用来存储键值对,思考为什么要用hashmap?
4 hashmap = {}
5
6 stack = []
7 i = 0
8 while i <= len(nums2) - 1:
9 if not stack:
10 stack.append(nums2[i])
11 i += 1
12 else:
13 if stack[-1] < nums2[i]:
14 while stack and stack[-1] < nums2[i]: # 别忘了添加and前的stack,防止溢出
15 x = stack.pop()
16 hashmap[x] = nums2[i]
17 else:
18 stack.append(nums2[i])
19 i += 1
20
21 for i in range(len(stack)):
22 hashmap[stack.pop()] = -1
23
24 # print(hashmap)
25
26 ans = [hashmap[nums1[i]] for i in range(len(nums1))]
27 # print(ans)
28
29 return ans
30

下一个更大元素 I

739. 每日温度

 1 class Solution:
2 def dailyTemperatures(self, T: List[int]) -> List[int]:
3
4 hashmap = {}
5
6 stack = []
7 i = 0
8 while i <= len(T)-1:
9 if not stack:
10 stack.append(i)
11 i += 1
12 else:
13 if T[stack[-1]] >= T[i]:
14 stack.append(i)
15 i += 1
16 else:
17 while stack and T[stack[-1]] < T[i]:
18 hashmap[stack.pop()] = i - stack[-1]
19
20 # 最后 可能栈中还有元素
21 for i in range(len(stack)):
22 hashmap[stack.pop()] = 0
23 # print(hashmap)
24 ans = [hashmap[i] for i in range(len(T))]
25
26 return ans

每日温度

605. 种花问题

 1 class Solution:
2 def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
3
4 # ans = 0
5 # i = 0
6 # 解法1:前后种花,1、0、【】、0、1
7 # flowerbed.insert(0,1)
8 # flowerbed.insert(1,0)
9 # flowerbed.append(0)
10 # flowerbed.append(1)
11 # while i <= len(flowerbed)-1:
12 # p = 0
13 # if flowerbed[i] == 0:
14 # i += 1
15 # p += 1
16 # while not flowerbed[i] and i <= len(flowerbed) -1:
17 # i += 1
18 # p += 1
19 # ans += math.ceil(max((p-2), 0) / 2)
20 # else:
21 # i += 1
22
23 # # print(ans)
24 # return True if ans>= n else False
25
26 # 解法2:贪心,能种就种
27 N = 0
28 L = len(flowerbed)
29 if not flowerbed:
30 return True
31 # 特殊情况处理
32 if L<2:
33 if flowerbed[0] == 0 or flowerbed[L-1] == 0:
34 N += 1
35 else:
36 # 头部
37 if not flowerbed[0] and not flowerbed[1]: # 第一个位置与第二个位置均为0
38 N += 1
39 flowerbed[0] = 1
40 # 尾部
41 if not flowerbed[L-1] and not flowerbed[L-2]: # 倒数第一个位置和倒数第二个位置均为0
42 N += 1
43 flowerbed[L-1] = 1
44
45 for i in range(1,L-1):
46 if flowerbed[i] == 0:
47 if flowerbed[i-1] == 0 and flowerbed[i+1] == 0:
48 N += 1
49 flowerbed[i] = 1
50
51 # print(N,flowerbed)
52 return True if N>=n else False
53

种花问题

chapter3 Resolution:动态规划

746. 使用最小花费爬楼梯

 1 class Solution:
2 def minCostClimbingStairs(self, cost: List[int]) -> int:
3
4 dp = [0] * len(cost)
5
6 L = len(cost)
7 if L == 1:
8 return 0
9 elif L == 2:
10 return min(cost[0], cost[1])
11
12 dp[0] = cost[0]
13 dp[1] = cost[1]
14
15 for i in range(2,L):
16 dp[i] = min(dp[i-1], dp[i-2]) + cost[i]
17
18 return min(dp[-1], dp[-2])

使用最小花费爬楼梯

62. 不同路径

 1 class Solution:
2 def uniquePaths(self, m: int, n: int) -> int:
3 # dfs 会 running out of time
4 # path = 0
5
6 # def backtrace(row, col):
7 # nonlocal path
8 # if row > m or col > n:
9 # return ;
10 # if row == m and col == n:
11 # path += 1
12 # return ;
13 # backtrace(row+1, col)
14 # backtrace(row, col+1)
15
16 # backtrace(1,1)
17 # return path
18
19 # 数论的方法
20 # down = 1 # 分母
21 # up = 1 # 分子
22 # A = m+n-2
23
24 # for i in range(m-1):
25 # down *= A
26 # A -= 1
27
28 # for i in range(1,(m-1)+1):
29 # up *= i
30
31 # return int(down/up)
32
33 # 动态规划方法
34 # dp[i][j] 到达第i行和第j列的方案数量,有多少种走法
35 dp = [[1] * m] * n
36
37 # 初始化,dp[:][0] = 0 and dp[0][:] = 1
38
39 for i in range(1,n):
40 for j in range(1,m):
41 dp[i][j] = dp[i][j-1] + dp[i-1][j]
42
43 # print(dp)
44 return dp[n-1][m-1]

不同路径

63. 不同路径 II (这个题目看出 “动态规划”五部曲当中  初始化的重要性,Success depends on details)

 1 class Solution:
2 def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
3 n = len(obstacleGrid)
4 m = len(obstacleGrid[0])
5
6 # import numpy as np
7 # dp = np.array([[0] * m] * n).tolist() # 不要将m和n的位置写反了,这种是正确的
8 # dp = [[0 for _ in range(m)] for _ in range(n)]
9 dp = []
10 for i in range(n):
11 t = []
12 for j in range(m):
13 t.append(0)
14 dp.append(t)
15
16 # dp = list([[0] * m] * n) # 这种不知道哪里错了?调试出来的结果感觉就是神奇的错误,试试测试用例[[0],[1]]
17 # dp = [[0] * m] * n # 同上
18 # print("dp", dp)
19
20 # 初始化
21 # 第一行是否有障碍
22 for i in range(m):
23 if obstacleGrid[0][i] == 0:
24 # 从i到最后都需要初始化为0
25 dp[0][i] = 1
26 else:
27 break
28
29 # 第一列是否有障碍
30 for i in range(n):
31 if obstacleGrid[i][0] == 0:
32 # 从j到最后都需要初始化为0
33 dp[i][0] = 1
34 else:
35 break
36 # print(dp)
37 for i in range(1, n):
38 for j in range(1, m):
39 if obstacleGrid[i][j] == 1:
40 dp[i][j] = 0
41 else:
42 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
43
44 # print(dp)
45
46 return dp[n - 1][m - 1]

不同路径 II

343. 整数拆分

 1 class Solution:
2 def integerBreak(self, n: int) -> int:
3 # dp[i] 表示拆分
4 dp = [0 for _ in range(n+1)]
5
6 # print(dp)
7 # 初始化特殊情况,考虑下文中的dp[2] = 1 和 dp[3] = 2两种情况
8 if n == 2:
9 return 1
10 elif n == 3:
11 return 2
12 # 0和1拆分不符合题目的要求,可以不对其进行初始化,dp[0] dp[1]
13 dp[2] = 1
14 dp[3] = 2
15
16 for i in range(4,n+1): # 一直到持续到求出dp[n]
17 for j in range(1,(i-2) + 1):
18 dp[i] = max(dp[i], max(j * (i-j),j * dp[i-j])) # 其实此处蕴含有求循环求最大值的意思
19
20 # print(dp)
21
22 return dp[n]

整数拆分

416. 分割等和子集

 1 class Solution:
2 def canPartition(self, nums: List[int]) -> bool:
3
4 # # 经动态规划转化后的题目
5 # m = len(nums)
6 # n = ceil(sum(nums) / 2)
7 # N = sum(nums) / 2
8 # if N != n:
9 # return False
10 # # print(m,n)
11
12 # dp = [[0 for _ in range(n+1)] for _ in range(m+1)] # 再一次倒在了n和mn弄混了
13 # # print(dp)
14
15 # for i in range(1,m+1):
16 # for j in range(1,n+1):
17 # if j < nums[i-1]:
18 # dp[i][j] = dp[i-1][j]
19 # else:
20 # A = dp[i-1][j]
21 # B = nums[i-1] + dp[i-1][j-nums[i-1]]
22 # dp[i][j] = max(A, B)
23
24 # # print(dp)
25 # if dp[i][j] == N:
26 # return True
27 # else:
28 # return False
29
30 # # 解法二:动态规划之滚动数组
31 if len(nums) == 0 or len(nums) == 1:
32 return False
33
34 m = len(nums)
35 n = int(sum(nums) / 2)
36
37 # print(n, N)
38 if int(sum(nums)) % 2 == 1:
39 return False
40 # print(m,n)
41
42 dp = [0 for _ in range(n+1)]
43 # print(dp)
44
45 for i in range(1,m+1):
46 for j in range(n,nums[i-1]-1,-1): # 细节range(4,0,-1)是[4,3,2,1],不包括0
47 A = dp[j]
48 B = nums[i-1] + dp[j-nums[i-1]]
49 # print(A, B)
50 dp[j] = max(A, B)
51 # print(dp)
52
53 if dp[n] == n:
54 return True
55 return False

分割等和子集

1049. 最后一块石头的重量 II

 1 class Solution:
2 def lastStoneWeightII(self, stones: List[int]) -> int:
3
4 m = len(stones)
5 sum_weight = int(sum(stones))
6
7 half_weight = int(sum_weight / 2)
8
9 dp = [0] * (half_weight+1)
10 # print(half_weight)
11
12 for i in range(m):
13 for j in range(half_weight, stones[i]-1, -1):
14 dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])
15 # print(dp)
16
17 # print(dp)
18 return abs((sum_weight - dp[-1]) - dp[-1])

最后一块石头的重量 II

474. 一和零

 1 class Solution:
2 def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
3
4 # 动态规划问题
5 # dp[i][j] 最多包含i个0,j个1的最大子集的长度为dp[i][j]
6
7 # dp[i][j] = max(dp[i][j], dp[i-zeroNum][j-oneNum] + 1)
8
9 dp = [[0 for _ in range(n+1)] for _ in range(m+1)]
10 # print("start",dp)
11
12 L = len(strs)
13 for i in range(L):
14
15 # 统计0的个数和1的个数
16 zero_num, one_num = 0, 0
17 for idx in range(len(strs[i])):
18 if strs[i][idx] == "0":
19 zero_num += 1
20 else:
21 one_num += 1
22 # print("len",zero_num, one_num)
23
24 for j in range(m, zero_num-1, -1):
25 for k in range(n, one_num-1, -1):
26 dp[j][k] = max(dp[j][k], dp[j-zero_num][k-one_num]+1)
27 # print("dp", dp)
28
29 return dp[m][n]

一和零

322. 零钱兑换 ,因为dp表示的是方案数,而与顺序无关,因此两种遍历顺序都是正确的:(1.先遍历nums再遍历target;2.先遍历target再遍历nums)

 1 class Solution:
2 def coinChange(self, coins: List[int], amount: int) -> int:
3
4 # 目标:达到target的目标的硬币最少
5
6 # dp[j]表示凑成了j的硬币最少数
7 # nums = coins, target = amount
8 # dp[j] = min(dp[j],dp[j-nums[i]]+1)
9
10 dp = [10000000] * (amount+1)
11 dp[0] = 0
12 print(dp)
13
14 for i in range(len(coins)):
15 for j in range(coins[i],amount+1):
16 dp[j] = min(dp[j],dp[j-coins[i]]+1)
17
18 # print(dp)
19
20 # print("最终答案是:",dp)
21 if dp[amount] == 10000000:
22 return -1
23 return dp[amount]

零钱兑换

279. 完全平方数

 1 class Solution:
2 def numSquares(self, n: int) -> int:
3
4 # dp[j]表示和为j的最少数量
5
6 # dp[j] = min(dp[j],dp[j-i*i]+1)
7
8 dp = [1000000] * (n+1)
9 dp[0] = 0
10 # print("init", dp)
11
12 # 完全背包,每个背包可以重复使用
13 max_nums2 = int(sqrt(n))
14 # print(max_nums2)
15
16 for i in range(1,max_nums2+1):
17 for j in range(i*i,n+1):
18 dp[j] = min(dp[j],dp[j-i*i]+1)
19
20 # print(dp)
21
22 # if dp[n] == 1000000:
23 # return 0
24 return dp[n]

完全平均数

139. 单词拆分

 1 class Solution:
2 def wordBreak(self, s: str, wordDict: List[str]) -> bool:
3
4 # 动态规划:完全背包问题,每件物品可以重复使用
5 # dp[j] 表示前j个字符都可以拆分成功
6
7 s_len = len(s)
8 wordDict_len = len(wordDict)
9 dp = [0] * (s_len + 1)
10 dp[0] = 1
11
12 for i in range(s_len+1):
13 for j in range(wordDict_len):
14 L = len(wordDict[j]) # 先知道字符串的长度
15 # print(L)
16 if i >= L:
17 substr = s[i-L:i] # 然后在s中取出L长度的字符串,看是否相等
18 if substr == wordDict[j] and dp[i-L] == 1:
19 dp[i] = 1
20 break
21 if dp[s_len] == 1:
22 return True
23 return False
24

单词拆分

2021年后

198. 打家劫舍

 1 class Solution:
2 def rob(self, nums: List[int]) -> int:
3
4 # # dp[i]表示包括num[i](表示nums[i]必须偷)的能够得到的最大金额,
5 # # 因此最终的最大金额是max(dp)
6 # if not nums:
7 # return 0
8 # dp = [0] * len(nums)
9 # # M = nums[0]
10 # for i in range(len(nums)):
11 # if i - 2 >= 0:
12 # M = max(dp[:i-1])
13 # else:
14 # M = 0
15 # dp[i] = M + nums[i]
16
17 # # print(dp)
18 # return max(dp)
19
20 # 方法二:dp[i]表示考虑到前i个(包括i)能够达到的最大金额
21 # dp[i] = max(dp[i-1],dp[i-2] + nums[i])
22 if len(nums) == 0:
23 return 0
24 if len(nums) == 1:
25 return nums[0]
26
27 dp = [0] * len(nums)
28 dp[0] = nums[0]
29 dp[1] = max(nums[0], nums[1])
30
31 for i in range(2,len(nums)):
32 dp[i] = max(dp[i-1],dp[i-2]+nums[i])
33
34 # print(dp)
35 return dp[len(nums)-1]

打家劫舍

121. 买卖股票的最佳时机

 1 class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3
4 # dp[i][0] 第i天(i从0开始,第0天,第1天...第i-1天,i属于【0,n-1】)若持有股票的所得最大现金
5 # dp[i][1] 第i天若不持有股票的所得最大现金
6 # dp[i][0] 由以上两种情况得出:1.昨天就已经持有股票的所得最大金额;2.当天(第i天)买了股票,所以当天的现金是-prices[i]
7 # dp[i][0] = max(dp[i-1][0], -prices[i])
8 # dp[i][1] 由以上两种情况得出:1.昨天就不持有股票的所得最大金额;2.i-1天持有股票的最大金额加上prices[i]
9 # dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i])
10
11 # 因此初始化
12 # 当i = 1时,dp[1][0] = max(dp[0][0], -prices[0])
13 # 当i = 1时,dp[1][1] = max(dp[0][1], dp[0][0]+prices[0])
14 # 因此初始化dp[0][0],dp[0][1]
15
16
17 # dp[][]看起来是二维数组,但是实际上表示的是一维数组,因为第二位已经用两个dp数组表示了
18 dp_holdStamp = [0] * len(prices)
19 dp_not_holdStamp = [0] * len(prices)
20
21 dp_holdStamp[0] = -prices[0]
22 dp_not_holdStamp[0] = 0
23
24 for i in range(1,len(prices)):
25 dp_holdStamp[i] = max(dp_holdStamp[i-1], -prices[i])
26 dp_not_holdStamp[i] = max(dp_not_holdStamp[i-1], dp_holdStamp[i-1]+prices[i])
27
28 # print(dp_holdStamp)
29 # print(dp_not_holdStamp)
30 return dp_not_holdStamp[len(prices)-1]
31
32 # 还可以继续优化,优化为滚动数组,向0-1背包那样

买卖股票的最佳时机

 1 class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3
4 # dp[i][0] 第i天(i从0开始,第0天,第1天...第i-1天,i属于【0,n-1】)若持有股票的所得最大现金
5 # dp[i][1] 第i天若不持有股票的所得最大现金
6 # dp[i][0] 由以上两种情况得出:1.昨天就已经持有股票的所得最大金额;2.当天(第i天)买了股票,所以当天的现金是-prices[i]
7 # dp[i][0] = max(dp[i-1][0], -prices[i])
8 # dp[i][1] 由以上两种情况得出:1.昨天就不持有股票的所得最大金额;2.i-1天持有股票的最大金额加上prices[i]
9 # dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i])
10
11 # 因此初始化
12 # 当i = 1时,dp[1][0] = max(dp[0][0], -prices[0])
13 # 当i = 1时,dp[1][1] = max(dp[0][1], dp[0][0]+prices[0])
14 # 因此初始化dp[0][0],dp[0][1]
15
16
17 # dp[][]看起来是二维数组,但是实际上表示的是一维数组,因为第二位已经用两个dp数组表示了
18 dp_holdStamp = [0] * len(prices)
19 dp_not_holdStamp = [0] * len(prices)
20
21 dp_holdStamp[0] = -prices[0]
22 dp_not_holdStamp[0] = 0
23
24 for i in range(1,len(prices)):
25 dp_holdStamp[i] = max(dp_holdStamp[i-1], -prices[i])
26 dp_not_holdStamp[i] = max(dp_not_holdStamp[i-1], dp_holdStamp[i-1]+prices[i])
27
28 # print(dp_holdStamp)
29 # print(dp_not_holdStamp)
30 return dp_not_holdStamp[len(prices)-1]
31
32 # 还可以继续优化,优化为滚动数组,向0-1背包那样

买卖股票的最佳时机

chapter4 Resolution:回溯

131. 分割回文串

 1 class Solution:
2 def partition(self, s: str) -> List[List[str]]:
3
4 s_len = len(s)
5
6 path = []
7
8 def backtrace(s, s_len, startIndex, tmp_path):
9 if startIndex == s_len:
10 # print(tmp_path[:])
11 path.append(tmp_path[:])
12 return ;
13
14 for i in range(startIndex + 1, s_len + 1):
15 substr = s[startIndex: i] # 检查所切割的子串是否是回文串
16 # print("AAA", i, substr)
17 if not check(substr): continue; # 一开始continue处写成了break,找了我四五个小时的错误!
18 tmp_path.append(substr)
19 backtrace(s, s_len, i, tmp_path)
20 tmp_path.pop()
21
22
23 def check(s):
24 # 首先求出s的长度L
25 L = len(s)
26
27 left_p, right_p = 0, L - 1
28 while left_p <= right_p:
29 if s[left_p] == s[right_p]:
30 left_p += 1
31 right_p -= 1
32 else:
33 return False
34
35 return True
36
37 backtrace(s, s_len, 0, [])
38
39 # print("ans", path)
40 return path

分割字符串

chapter5 Resolution:贪心

455. 分发饼干

 1 class Solution:
2 def findContentChildren(self, g: List[int], s: List[int]) -> int:
3
4 g_sort = sorted(g)
5 s_sort = sorted(s)
6 # print("sort", g_sort, s_sort)
7
8 # 两个指针
9 pg = len(g_sort) - 1
10 ps = len(s_sort) - 1
11 # print("p", pg, ps)
12
13 num_ok = 0
14 while pg >=0 and ps >= 0:
15 if g_sort[pg] <= s_sort[ps]:
16 pg -= 1
17 ps -= 1
18 num_ok += 1
19 else:
20 pg -= 1
21
22 return num_ok
23
24

分发饼干

45. 跳跃游戏 II

 1 class Solution:
2 def jump(self, nums: List[int]) -> int:
3
4 MAX = 1000000
5 dp = [MAX] * len(nums)
6
7 dp[0] = 0
8
9 for i in range(len(nums)):
10 if i + nums[i] >= len(nums)-1: # 如果i + nums[i] 超过了nums的长度
11 mi = len(nums)-1
12 else:
13 mi = i + nums[i]
14
15 for j in range(i+1,mi+1): # i 给 后面的nums[i]可以进行松弛
16 dp[j] = min(dp[j], dp[i] + 1)
17
18
19 # print(dp[len(nums)-1])
20 return dp[len(nums)-1]

跳跃游戏 2

chapterX Resolution:哈希表

剑指 Offer 50. 第一个只出现一次的字符

chapterX Resolution:拓扑排序

207. 课程表 (拓扑排序的典范(模板))

 1 class Solution:
2 def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
3
4 # 首先统计n门课程的入度为多少
5 in_degree = [0] * numCourses
6 for i in range(len(prerequisites)):
7 x = prerequisites[i][0]
8 in_degree[x] += 1
9 # print(in_degree)
10
11 # 初始化:将入度为0的课程入栈
12 stack = []
13 for i in range(numCourses):
14 if in_degree[i] == 0:
15 stack.append(i)
16 # print(stack)
17
18 # 要是初始化之后发现没有入度为0的课程,则直接返回False
19 if not stack:
20 return False
21
22 # 拓扑排序的core code
23 while stack:
24 x = stack.pop()
25 for i in range(len(prerequisites)):
26 if prerequisites[i][1] == x:
27 in_degree[prerequisites[i][0]] -= 1
28
29 if in_degree[prerequisites[i][0]] == 0:
30 stack.append(prerequisites[i][0])
31 # print(stack)
32
33 # 经过core code之后,如果入度表还存在非0值(表示还存在入度不为0的课程),说明已经形成环,返回False
34 for i in range(len(in_degree)):
35 if in_degree[i] != 0:
36 return False
37
38 return True
39 # 最后,1.效率比较低,需要改进
40 # 2.拓扑排序的代码一般可以作为模板使用
41

课程表

210. 课程表 II

课程表 II

chapterX Resolution:双指针

830. 较大分组的位置

 1 class Solution:
2 def largeGroupPositions(self, s: str) -> List[List[int]]:
3 i = 0
4 start = 0
5 end = 0
6 ans = []
7 # 看起来两层while循环,实际上只对数组遍历了一次
8 while i <= len(s)-1:
9 start = i
10 end = start
11 while i+1 <= len(s)-1 and s[i] == s[i+1]:
12 end += 1
13 i += 1
14 if end - start >= 2:
15 ans.append([start,end])
16 i = end+1 # 别忘了这个重要的语句
17
18 return ans

较大分组的位置

392. 判断子序列

 1 class Solution:
2 def isSubsequence(self, s: str, t: str) -> bool:
3
4 if len(s) == 0 and len(t) == 0:
5 return True
6 sp = 0
7 tp = 0
8
9 tp_flag = 0 # tp_flag指示是否遍历完t,若是则赋值为1,结束两层while循环
10 while sp <= len(s) - 1:
11 flag = 0 # flag表示当前字符是否匹配,加他的原因,“a”,“x”这样的测试集
12 while True:
13 if tp > len(t) - 1:
14 tp_flag = 1
15 break
16 if s[sp] == t[tp]:
17 flag = 1
18 break
19 else:
20 tp += 1
21 if flag:
22 tp += 1
23 sp += 1
24 if tp_flag:
25 break
26
27 if sp == len(s):
28 return True
29 else:
30 return False
31 # print(sp)

判断子序列

1. 两数之和

 1 class Solution:
2 def twoSum(self, nums: List[int], target: int) -> List[int]:
3 hashtable = dict()
4 for index,value in enumerate(nums):
5 if target - value in hashtable:
6 return [hashtable[target-value],index]
7 hashtable[value] = index # 这句话不能放在上一个IF前,因为可能出现刚放进去一个3(target=6),就会立马与6-3=3(与自己的3匹配了)匹配
8 #print(hashtable)
9
10 return []
11
12 #判断某个数在不在字典里,复杂度是O(1)
13
14 #本算法用hash这样加快了寻找target-value的速度,这样时间复杂度就下降到n
15 #空间复杂度上升到n

两数之和

最新文章

  1. Scrapy框架实现爬虫
  2. 1003. Emergency
  3. win7系统扩展双屏幕时,如何在两个屏幕下都显示任务栏
  4. xmind的第三天笔记
  5. emlog模板制作帮助手册
  6. 巧用ifstream判断文件是否存在
  7. C++类中的静态成员变量与静态成员函数
  8. 修改 “嗨加游-Prefix.pch” 或者 “嗨加游-Info.plist ” 方法
  9. 不借助第三方jar包实现文件上传
  10. Javascript Prototype __proto__ constructor 三者的关系
  11. Online Judge(OJ)搭建——2、数据库,SQL语句
  12. .htaccess rewrite 规则详细说明
  13. 从yield 到yield from再到python协程
  14. Java高级特性 第1节 集合框架和泛型
  15. Tomcat通过Redis实现session共享的完整部署记录
  16. hadoop的输入和输出文件
  17. delphi 新版内存表 FDMemTable
  18. linux命令(44):sed,vim;去掉文件中的^M 符号,去掉行首空格和制表符
  19. 编译环境搭建:Makefile
  20. 180725-InfluxDB-v1.6.0安装和简单使用小结

热门文章

  1. KMP字符串 AcWing 831
  2. vue还可以这样写
  3. 简述traceroute命令的原理
  4. rn用Modal实现Drawer
  5. eclipse项目转idea项目
  6. JS的有关递归的知识点(数据无限级联的实现)
  7. Spark应用程序第三方jar文件依赖解决方案
  8. 以EEPROM为例的硬件IIC的使用
  9. if语法案例
  10. 073_SFDC Limit