Self-Learned Algorithms - 01
This is my learning note about algorithms.
Reference
- 《小浩算法》) - 数组系列
350.两个数组的交集·2
https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/
题解
- 简单的求交集的映射类题目,一开始想错的点就在于说明中的“输出结果中每个元素出现的次数”,而不是简单的出现元素。映射关系为<元素,出现次数>。
- 进阶中“如果给定的数组已经排好序”是一个很大的提示,我们可以先给数组排序,再进行计算。
解法
- 暴力解法:直接枚举
1 | if len(nums2) < len(nums1): |
- 哈希表
1 | from collections import Counter |
第5行中还可以用(count := hs.get(num, 0)) > 0
进行判断。
- 排序双指针
1 | nums1.sort() |
或者可以不用新建数组,而是使用一个index
将需要输出的数字替换到nums1[index]
,最终输出nums1[:index]
即可。(实际空间复杂度相同,但时间会增加)
- 一个一行解法
1 | return sum(([i] * j for i, j in (collections.Counter(nums1) & collections.Counter(nums2)).items()), []) |
对于两个 Counter 对象,&
操作意味着取两者都有的key, value取小的那一个。
14.最长公共前缀
https://leetcode-cn.com/problems/longest-common-prefix/
题解
解法
- 水平扫描
1 | if len(strs) == 0: |
- 垂直扫描
1 | if len(strs) == 0: |
- 最大和最小字符串的公共前缀
1 | if not strs: |
min()
和max()
是找到数组中字典序最小和最大的字符串。一个很新奇的思路。(时间减少,内存消耗增加)
- zip迭代
1 | s = "" |
zip 打包成元素列表,同时利用set()函数的不重复性。
官方文档对zip(*iteration)
的解释:创建一个聚合了来自每个可迭代对象中的元素的迭代器。 返回一个元组的迭代器,其中的第 i 个元组包含来自每个参数序列或可迭代对象的第 i 个元素。 当所输入可迭代对象中最短的一个被耗尽时,迭代器将停止迭代。 当只有一个可迭代对象参数时,它将返回一个单元组的迭代器。 不带参数时,它将返回一个空迭代器。
(但时间和内存消耗都比水平扫描要多)
122.买卖股票的最佳时机·2
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
题解
- 不能参与多笔交易。换句话讲,我们只能在手上没有股票的时候买入
- 尽可能地多进行交易。把握住机会,在每一次涨跌的时候,低价卖入高价卖出,就可以使利益达到最大化
解法
- 线性规划:第
i
天只有两种状态,不持有或持有股票,当天不持有股票的状态可能来自昨天卖出或者昨天也不持有,同理,当天持有股票的状态可能来自昨天买入或者昨天也持有中,取最后一天的不持有股票状态就是问题的解
1 | if not prices: |
但dp的运行时间和内存消耗都要大。
- 贪心:算法题(×) 脑筋急转弯题( √ ),只要今天价格小于明天价格就在今天买入然后明天卖出
1 | ans = 0 |
189.旋转数组
https://leetcode-cn.com/problems/rotate-array/
题解
- 要求使用空间复杂度为 O(1) 的原地算法。(限制了直接拼接构造新数组sad不过还是有trick可以的)
- 至少有三种不同的方法可以解决这个问题。
解法
- 一行切片:按着切片下标修改值,而不是nums修改的地址重新指向右边的临时地址(不写切片无法AC,因为不是原地)
1 | nums[:] = nums[-k % len(nums):] + nums[:-k % len(nums)] |
或者(空间O(1),速度提升)
1 | nums[:] = (nums[i] for i in range(-(k % len(nums)), len(nums) - k % len(nums))) |
k%n
是为了简化计算,比如数组长度为 7,旋转 8 位其实就相当于旋转 1 位,这样减少了循环次数
- 三次反转:反转前
n-k
个,反转后k
个,反转全部
1 | n = len(nums) |
(时间和空间上都比一行切片要优秀)
1 | count,index,temp = 0,0,nums[0] |
27.原地删除
https://leetcode-cn.com/problems/remove-element/
题解
- 没什么好说的,只能说python天下第一吧(?)
解法
- 很暴力
1 | while val in nums: |
- 倒序遍历:在正向遍历的过程中,删除list的多个元素,index值不能变,比较麻烦。反向遍历就不会存在这个问题。
1 | for i in range(len(nums)-1, -1, -1): |
- 双指针
1 | i = 0 |
类似题目
26.删除排序数组中的重复项
283.移动O
66.加一
https://leetcode-cn.com/problems/plus-one/
题解
- 看起来很简单的题目,只要考虑两种情况:9和其他数字就可以了
解法
- 一次遍历:遍历是否为9,是9变0,不是9返回即可
1 | for i in range(1,len(digits)+1): |
- python抖机灵法
1 | a = [i *10**index for index,i in enumerate(digits[::-1])] |
1 | nums_str = "" |
(抖机灵法2的内存消耗居然更少呢)
1.两数之和
https://leetcode-cn.com/problems/two-sum/
题解
- 可以暴力解,但是有没有更好的办法呢?
解法
- 暴力解法:最简单的暴力解法就是两层循环,两数相加再判断是否为目标值,但其实一层循环寻找
target - x
就可以了
1 | n = len(nums) |
- 哈希表:如果有重复值字典也会刷新
1 | dct = {} |
如果先把数组转化成字典的话,target-n
的值很有可能会等于n
,所以放在后面更新字典。(哈希解法的速度比暴力快20倍)