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

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

于是,我找了一个平台,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

两数之和

LeetCode习题集的更多相关文章

  1. 我为什么要写LeetCode的博客?

    # 增强学习成果 有一个研究成果,在学习中传授他人知识和讨论是最高效的做法,而看书则是最低效的做法(具体研究成果没找到地址).我写LeetCode博客主要目的是增强学习成果.当然,我也想出名,然而不知 ...

  2. LeetCode All in One 题目讲解汇总(持续更新中...)

    终于将LeetCode的免费题刷完了,真是漫长的第一遍啊,估计很多题都忘的差不多了,这次开个题目汇总贴,并附上每道题目的解题连接,方便之后查阅吧~ 477 Total Hamming Distance ...

  3. [LeetCode] Longest Substring with At Least K Repeating Characters 至少有K个重复字符的最长子字符串

    Find the length of the longest substring T of a given string (consists of lowercase letters only) su ...

  4. Leetcode 笔记 113 - Path Sum II

    题目链接:Path Sum II | LeetCode OJ Given a binary tree and a sum, find all root-to-leaf paths where each ...

  5. Leetcode 笔记 112 - Path Sum

    题目链接:Path Sum | LeetCode OJ Given a binary tree and a sum, determine if the tree has a root-to-leaf ...

  6. Leetcode 笔记 110 - Balanced Binary Tree

    题目链接:Balanced Binary Tree | LeetCode OJ Given a binary tree, determine if it is height-balanced. For ...

  7. Leetcode 笔记 100 - Same Tree

    题目链接:Same Tree | LeetCode OJ Given two binary trees, write a function to check if they are equal or ...

  8. Leetcode 笔记 99 - Recover Binary Search Tree

    题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...

  9. Leetcode 笔记 98 - Validate Binary Search Tree

    题目链接:Validate Binary Search Tree | LeetCode OJ Given a binary tree, determine if it is a valid binar ...

  10. Leetcode 笔记 101 - Symmetric Tree

    题目链接:Symmetric Tree | LeetCode OJ Given a binary tree, check whether it is a mirror of itself (ie, s ...

随机推荐

  1. JZOJ 5350. 【NOIP2017提高A组模拟9.7】陶陶摘苹果

    题目 分析 很神奇的事情又发生了!! 很容易想到设 \(f_{i,j}\) 表示考虑前 \(i\) 个区间,已选 \(j\) 个区间且必选第 \(i\) 时能覆盖到的最多苹果数 转移 \(O(n)\) ...

  2. 基于pytorch实现模型剪枝

    一,剪枝分类 1.1,非结构化剪枝 1.2,结构化剪枝 1.3,本地与全局修剪 二,PyTorch 的剪枝 2.1,pytorch 剪枝工作原理 2.2,局部剪枝 2.2.1,局部非结构化剪枝 2.2 ...

  3. .Net 调用 sqlserver 存储过程实例

    1.输出datatable 存储过程: create proc inparamS @inpar varchar(20) as begin select top 1 * from Address whe ...

  4. XAF在页面中添加按钮

    参考链接:XAF在DashboardView右下方添加SimpleAction - 幽梦紫曦的专栏 - TNBLOG

  5. javaWeb学习一

    web开发(web就是网页): 静态web html.css 提供给所有人看到数据不会改变 动态web 提供给所有人看到数据会改变,不同用户.不同时间和地点都会不同 技术栈:Servlet/JSP,A ...

  6. DRF全局总结

    基础部分 一.创建工程 1.创建虚拟环境 python -m venv 路径 2.安装Django 进入虚拟环境 pip install Django 3.创建项目 django-admin star ...

  7. Python爬取亚马逊商品页面

    仍然利用Requests库来实现 1 import requests 2 r=requests.get('https://www.amazon.cn/gp/product/B01M8L5Z3Y') 3 ...

  8. ComWin’ round 11部分题解

    https://vjudge.net/contest/325913#overview A.Threehouses 题意:一直二维平面上的$n$个点中,前$e$个点落在小岛周围,并且有$p$条边已经连接 ...

  9. Spring全家桶(一)之Spring总结

    0. Spring常用注解汇总 Spring常用注解汇总 使用注解之前要开启自动扫描功能,其中base-package为需要扫描的包(含子包). <context:component-scan ...

  10. Qt5.6使用Qt自带虚拟键盘

    Qt自带虚拟键盘是5.7版本以上才有,要在Qt5.6上使用自带虚拟键盘需要先下载源码,再进行编译安装.上网查了一些资料都很有用. https://doc.qt.io/qt-5/qtvirtualkey ...