26. Remove Duplicates from Sorted Array
删除排序数组中重复的元素, 在原数组上操作,返回一个长度,标识前n个元素为目标数组。原题
1 | Given nums = [0,0,1,1,1,2,2,3,3,4], |
1 | def remove_duplicates(nums): |
80. Remove Duplicates from Sorted Array II
和上题一样,但是可以允许重复两次。原题
方法一:双指针调了半天。
1 | def removeDuplicates(self, nums: List[int]) -> int: |
1 | def removeDuplicates(self, nums: List[int]) -> int: |
66. Plus One
给数组加一,元素为非负整数,不以0开头,每个元素只有一个数字。原题
1 | Input: [4,3,2,1] |
1 | def plusOne(self, digits: 'List[int]') -> 'List[int]': |
方法二:Math 进位
1 | def plus_one(digits): |
1 | def plusOne(self, digits: 'List[int]') -> 'List[int]': |
989. Add to Array-Form of Integer
和66题很相似。不同的是这个K会大于10,以至于余数也会大于10。原题
1 | Input: A = [2,7,4], K = 181 |
方法一:竞赛写法。
1 | def addToArrayForm(self, A: 'List[int]', K: 'int') -> 'List[int]': |
方法二:原理实现。
1 | def addToArrayForm(self, A: 'List[int]', K: 'int') -> 'List[int]': |
88. Merge Sorted Array
合并两个有序数组,在nums1上修改。原题
1 | Input: |
1 | def merge(self, nums1, m, nums2, n): |
118. Pascal’s Triangle
杨辉三角。原题
1 | Input: 5 |
1 | def generate(self, numRows: 'int') -> 'List[List[int]]': |
方法二
1 | def generate(num): |
119.Pascal’s Triangle II
杨辉三角,只打印一层。原题
1 | def getRow(self, rowIndex: 'int') -> 'List[int]': |
169. Majority Element
找出数组中出现次数超过一半的元素。原题
方法一:排序. Time-O(nlogn), Space-O(n)
1 | def majority_element(nums): |
方法二:Counter Time-O(n), Space-O(n)
1 | def majority_element(nums): |
1 | def majorityElement(self, nums): |
229. Majority Element II
找到数组中出现超过n/3次的元素。原题
1 | Input: [1,1,1,3,3,2,2,2] |
方法一:波义尔摩尔投票法同样可用,但是我一开始想一次遍历求,发现好像不可以,最后都要遍历一次判断是否满足条件。循环中是elif,两个候选人开始设为不同的值以用来区分。
1 | def majorityElement(self, nums: List[int]) -> List[int]: |
方法二:Counter. by Stefan.
1 | def majorityElement(self, nums: List[int]) -> List[int]: |
189. Rotate Array
旋转数组。进阶:使用O(1)空间实现。原题
1 | Input: [1,2,3,4,5,6,7] and k = 3 |
1 | def rotate(self, nums: List[int], k: int) -> None: |
方法二:reverse的方法很新颖,不过要遍历两次数组。
1 | def rotate(self, nums: List[int], k: int) -> None: |
1 | def rotate(self, nums: List[int], k: int) -> None: |
217. Contains Duplicate
数组中是否包含重复元素。原题
1 | Input: [1,2,3,1] |
方法一:set
1 | def contains_duplicate(nums): |
方法二:hash
1 | def containsDuplicate(self, nums: 'List[int]') -> 'bool': |
219. Contains Duplicate II
数组中是否包含重复元素,且元素下标差小于等于k。原题
1 | Input: nums = [1,2,3,1], k = 3 |
思路:开始想用set作切片来判断,同上题方法一,但是效率太低。故使用字典。
1 | def containsNearbyDuplicate(self, nums, k): |
220. Contains Duplicate III
是否存在索引差k范围内的绝对值不大于t的两个值。原题
1 | Input: nums = [1,2,3,1], k = 3, t = 0 |
方法一:实际上是桶排序的原理,每个桶的size为t。两个差值为t的的数,只可能出现在同一个桶或者两边的桶中。每个桶只维护一个值就行了,因为如果有两个值,那么肯定就返回了。
1 | def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool: |
283. Move Zeroes
将数组0元素移动到末尾,保证其他元素顺序。原题
1 | Input: [0,1,0,3,12] |
方法一:two pointers
1 | def move_zero(nums): |
方法二: slicing
1 | def move_zero(nums): |
1 | def moveZeroes(self, nums: 'List[int]') -> 'None': |
方法四:排序。时间复杂度略高。
1 | def moveZeroes(self, nums: 'List[int]') -> 'None': |
54. Spiral Matrix
螺旋矩阵,顺时针打印矩阵。原题
这里注意一点matrix.pop(0)
需要转成list,因为zip函数中的每个元素是一个tuple,如果不转变成了一个tuple+list
,会抛出异常。
ps: 此题解法为LeetCode一位大神,经常能看到此人的答案,不过这个是我认为最pythonic的一个,没有为了强行one-line而one-line。brilliant!
1 | TypeError: can only concatenate tuple (not "list") to tuple |
1 | def spiralOrder(self, matrix): |
方法二:迭代写法。
1 | def spiralOrder(self, matrix: List[List[int]]) -> List[int]: |
此题有个变形,如果逆时针该如何打印。这样的话情况稍微复杂一些。
1 | def anti_clock_wise(self, matrix) |
59. Spiral Matrix II
按照顺时针的顺序生成一个矩阵。原题
1 | Input: 3 |
方法一:自己的方法。使用了一个控制方向,如果超范围或者有值就换方向。1
2
3
4
5
6
7
8
9
10
11
12
13
14def generateMatrix(self, n: int) -> List[List[int]]:
ans = [[0]*n for _ in range(n)]
op = itertools.cycle([(0, 1), (1, 0), (0, -1), (-1, 0)])
d = next(op)
x, y = (0, 0)
for k in range(1, n**2+1):
ans[x][y] = k
i, j = x+d[0], y+d[1]
if not 0<=i<n or not 0<=j<n or ans[i][j]:
d = next(op)
x, y = x+d[0], y+d[1]
else:
x, y = i, j
return ans
方法二:stefan的旋转法,我往这边想了,zip也想到了,没想到的是,从里往外遍历,还有一点是根据A的长度确定起始点。
1 | || => |9| => |8| |6 7| |4 5| |1 2 3| |
1 | def generateMatrix(self, n: int) -> List[List[int]]: |
885. Spiral Matrix III
从二维数组中的某一个点开始顺时针旋转输出所有的坐标。原题
方法一:还是通过生成器控制方向,当处于水平位置时步数增加1。1 | def spiralMatrixIII(self, R: int, C: int, r0: int, c0: int) -> List[List[int]]: |
di, dj = dj, -di
刚好是这个右转的方向。然后用一个n来计算步数。
1 | def spiralMatrixIII(self, R: int, C: int, r0: int, c0: int) -> List[List[int]]: |
方法三:Lee的几何方法。根据到目标点的距离大小排序。
1 | def spiralMatrixIII(self, R, C, r, c): |
53. Maximum Subarray
连续子数组的最大和。原题
1 | Input: [-2,1,-3,4,-1,2,1,-5,4], |
方法一:书中的思想。
1 | def maxSubArray(self, nums): |
accumulate
是把函数放到后面的。
1 | def maxSubArray(self, nums): |
918. Maximum Sum Circular Subarray
连续数组的最大和,数组可以首位相连,但是最大长度不能超过一个数组。原题
1 | Input: [1,-2,3,-2] |
方法一:by @Lee215。看完这个解法豁然开朗,只需要同时找到一个累加和最小的子数组,再用总数减掉。
1 | def maxSubarraySumCircular(self, A: List[int]) -> int: |
904. Fruit Into Baskets
实际上该题抽象为求最大滑动窗口的长度,滑动窗口不同元素最多不超过两个。原题
1 | tree = [3,3,3,1,2,1,1,2,3,3,4] # 5 |
一开始没有找到滑动窗口的左边界,老是想直接删除一个key,后来看别人代码受到启发,可以用一个内循环来解决,可以逐个删除,然后判断是否为空。
1 | def totalFruit(self, tree): |
27. Remove Element
从数组中删除元素,在原数组修改,要求返回一个长度。原题
1 | Given nums = [0,1,2,2,3,0,4,2], val = 2, |
方法一:前后指针,r要从n
开始,以n-1
作比较,这里r不是从n-1
开始是因为nums=[]
的情况,否则l+1
将超出数组范围。
1 | def removeElement(self, nums, val): |
1 | def removeElement(self, nums, val): |
349. Intersection of Two Arrays
求两个数组的交集。返回的数组必须元素唯一,可以无序。原题
思路:一开始看到这题以为是两个链表求相交点。最后发现Intersection
不应该理解为“十字路口”而应该是“交集”。这里翻了一下discuss
,大部分都是使用方法一,其它方法要么太繁琐,要么效率低。值得注意的是,此题的相关话题还有一项是Binary Search
也就是说,可能会有一个较为高效的二分搜索法的实现方式。
1 | Example 2: |
1 | def intersection(self, nums1, nums2): |
1 | def intersection(self, nums1: 'List[int]', nums2: 'List[int]') -> 'List[int]': |
350. Intersection of Two Arrays II
和上题不同的是要返回所有的交集元素。原题
1 | Example 1: |
1 | def intersect(self, nums1, nums2): |
方法二:不使用Counter
。
1 | def intersect(self, nums1, nums2): |
last
即可。
905. Sort Array By Parity
将一个数组重新排列,是偶数在前奇数在后。原题
1 | Input: [3,1,2,4] |
1 | def sortArrayByParity(self, A): |
方法二:列表生成式。
1 | def sortArrayByParity(self, A): |
922. Sort Array By Parity II
输入一个奇数偶数数量相同的数组,返回下标和其对应的值都是奇数或偶数,即奇偶交叉。和905相似。原题
方法一:使用切片的特性赋值。1 | def sortArrayByParityII(self, A): |
1 | def sortArrayByParityII(self, A: 'List[int]') -> 'List[int]': |
933. Number of Recent Calls
输入一个时间t,返回3000毫秒内所有的请求个数。原题
方法一:deque.
1 | class RecentCounter: |
937. Reorder Log Files
按照规则将log文件排序。原题
1 | Input: ["a1 9 2 3 1","g1 act car","zo4 4 7","ab1 off key dog","a8 act zoo"] |
方法一:列表生成式。
1 | def reorderLogFiles(self, logs): |
1 | def reorderLogFiles(self, logs: 'List[str]') -> 'List[str]': |
485. Max Consecutive Ones
输入一个二进制数组,返回最大的连续1的长度。原题
1 | Input: [1,1,0,1,1,1] |
1 | def findMaxConsecutiveOnes(self, nums: 'List[int]') -> 'int': |
方法二:使用groupby。
1 | def findMaxConsecutiveOnes(self, nums): |
方法三:split。不过这个效率不高。
1 | def findMaxConsecutiveOnes(self, nums): |
1 | def findMaxConsecutiveOnes(self, nums): |
1 | def findMaxConsecutiveOnes(self, nums: List[int]) -> int: |
1004. Max Consecutive Ones III
与上题不同的是,有K次机会可以将0变成1. 原题
1 | Input: A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3 |
方法一:这题竞赛时没想出来,受上题影响,思路跑到了groupby
那里,想着怎么分组后操作。实际上此题完全不同,应该使用滑动窗口。
1 | def longestOnes(self, A: List[int], K: int) -> int: |
496. Next Greater Element I
找出数组nums2中对应的nums1元素的位置之后的第一个比nums1大的元素。nums1是nums2的子集,二者均无重复的元素。原题
1 | Input: nums1 = [4,1,2], nums2 = [1,3,4,2]. |
方法一:暴力法,因为题中给了范围数组长度小于1000,所以也没有超时。
1 | def nextGreaterElement(self, nums1, nums2): |
方法二:one-liner,生成器一开始想到了,没想到next函数还可以设默认值。
1 | def nextGreaterElement(self, nums1, nums2): |
1 | def nextGreaterElement(self, nums1, nums2): |
953. Verifying an Alien Dictionary
判断一个字符串数组是否按照特定的字典顺序排序。原题
1 | Input: words = ["hello","leetcode"], order = "hlabcdefgijkmnopqrstuvwxyz" |
1 | def isAlienSorted(self, words: 'List[str]', order: 'str') -> 'bool': |
1 | def isAlienSorted(self, words: 'List[str]', order: 'str') -> 'bool': |
506. Relative Ranks
根据得分,返回排名。前三要用奖牌表示。原题
1 | Input: [5, 4, 3, 2, 1] |
方法一:先生成一个排行榜单,再根据每个得分把排序映射上去。
1 | def findRelativeRanks(self, nums): |
1 | def findRelativeRanks(self, nums): |
532. K-diff Pairs in an Array
找出差为k的不重复的成对元素的个数。原题
1 | Input: [3, 1, 4, 1, 5], k = 2 |
方法一:Counter,一开始没想到,想sort或是set,然后实现不了。
1 | def findPairs(self, nums, k): |
961. N-Repeated Element in Size 2N Array
找出2N长的数组中重复N次的数字,其它数字均只出现一次。此题不能用波义尔摩尔投票因为数量没有大于半数。原题
1 | Input: [2,1,2,5,3,2] |
1 | def repeatedNTimes(self, A): |
2234
和2342
。此方法不能用于169题,因为169题中的其它元素是可能重复的。
1 | def repeatedNTimes(self, A: 'List[int]') -> 'int': |
967. Numbers With Same Consecutive Differences
根据规则生成一组数组,数字长度为N,每两位的差为K。原题
方法一:迭代生成,其实此题本是一道动态规划题,但由于解法不是,暂时归到数组里。
1 | def numsSameConsecDiff(self, N, K): |
1 | def numsSameConsecDiff(self, N, K): |
561. Array Partition I
将数组两两分成一组,累加每组的最小值,使之尽量大。原题
1 | Input: [1,4,3,2] |
1 | def arrayPairSum(self, nums): |
566. Reshape the Matrix
改变矩阵的形状,如果元素超出或不足,返回原矩阵。原题
1 | Input: |
方法一:扁平化后重组。
1 | def matrixReshape(self, nums, r, c): |
1 | def matrixReshape(self, nums, r, c): |
575. Distribute Candies
给姐姐弟弟分糖,两人数量一样,保证姐姐的种类最多,求姐姐最多能分到多少种。原题
1 | Input: candies = [1,1,2,2,3,3] |
1 | def distributeCandies(self, candies): |
594. Longest Harmonious Subsequence
最长的和谐子数组,即元素的最大值和最小值正好相差一的子数组,元素顺序无关,求最大子数组长度。原题
1 | Input: [1,3,2,2,5,2,3,7] |
方法一:开始想错了,后来发现子数组只能包含两个元素。
1 | def findLHS(self, nums): |
1 | def findLHS(self, nums): |
598. Range Addition II
这题看上去挺长,其实抽象起来很简单,把整个矩阵想象成一个积木,然后每次往上叠加,每次叠放的矩形都会和左上角对齐,所以最后完成的时候,左上角的一小块一定是最“厚”的,问题就变成和左上一样的厚度的面积。实际上等于叠放的所有矩形的最小长度和最小宽度的乘积。原题
1 | Input: |
1 | def maxCount(self, m, n, ops): |
599. Minimum Index Sum of Two Lists
找出两个人共同最喜欢的餐厅。如果有多个输出多个。原题
1 | Input: |
方法一:这里做了一个优化,以原list1的顺序输出数组,如果索引太大超出了最小索引和,这样即使是map2使用第一个元素也无法满足条件,直接退出。
1 | def findRestaurant(self, list1, list2): |
605. Can Place Flowers
是否可以种下给定数量的花。两颗花不能挨着,给定的数组中满足这一条件。原题
1 | Input: flowerbed = [1,0,0,0,1], n = 2 |
1 | def canPlaceFlowers(self, flowerbed, n): |
643. Maximum Average Subarray I
最大的连续的长度为k的子数组的平均值。原题
1 | Input: [1,12,-5,-6,50,3], k = 4 |
方法一:参考了stefen大神的答案,自己写的滑动窗口居然超时了。accumulate也不是想不到,此答案厉害的地方在于 补0 和map操作。像这种固定长度的滑动窗口使用补0的accumulate
,可以用到其他的题上。
1 | def findMaxAverage(self, nums, k): |
661. Image Smoother
使图片平滑模糊,一个矩阵使每个点的值为周围所有点的平均值,包括自己。原题
1 | Input: |
方法一:参考了评论区一位朋友的写法,不过效率不是很高,800ms,Solution给出的方法也是这个速度,看来如果优化的话,可能使用numpy
会好一点。
1 | def imageSmoother(self, M): |
665. Non-decreasing Array
判断是否改变一个数,可使其变成单调递增数组。原题
1 | Input: [4,2,3] |
1 | def checkPossibility(self, nums): |
- If p = 0, then we could make the array good by setting A[p] = A[p+1]
- if p = len(A) - 2, then we could make the array good by setting A[p+1] = A[p]
- Otherwise, A[p-1], A[p], A[p+1], A[p+2] all exist, and:
- change A[p] to be between A[p-1] and A[p+1] if possible, or: [4, 8, 6]
- change A[p+1] to be between A[p] and A[p+2] if possible. [4, 5, 3, 6]
674. Longest Continuous Increasing Subsequence
最长连续递增子数组长度。原题
1 | Input: [1,3,5,4,7] |
1 | def findLengthOfLCIS(self, nums: 'List[int]') -> 'int': |
682. Baseball Game
棒球游戏,给了一些积分规则。原题
1 | Input: ["5","2","C","D","+"] |
1 | def calPoints(self, ops): |
690. Employee Importance
员工重要值。原题
1 | Input: [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1 |
1 | def getImportance(self, employees, id): |
方法二:recursively.
1 | def getImportance(self, employees, id): |
724. Find Pivot Index
找到中心索引,使得左右两边和相等。左右求和时均不包含该索引。原题
1 | Input: |
方法一:指针。
1 | def pivotIndex(self, nums): |
1 | def pivotIndex(self, nums): |
985. Sum of Even Numbers After Queries
计算Queries后,累加所有的偶数。原题
1 | Input: A = [1,2,3,4], queries = [[1,0],[-3,1],[-4,0],[2,3]] |
1 | def sumEvenAfterQueries(self, A: 'List[int]', queries: 'List[List[int]]') -> 'List[int]': |
986. Interval List Intersections
两个区间列表求相交。原题
1 | Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]] |
1 | def intervalIntersection(self, A: 'List[Interval]', B: 'List[Interval]') -> 'List[Interval]': |
747. Largest Number At Least Twice of Others
最大的数是否大于等于所有其它数的两倍。原题
1 | Input: nums = [3, 6, 1, 0] |
1 | def dominantIndex(self, nums: 'List[int]') -> 'int': |
766. Toeplitz Matrix
Toeplitz矩阵,即对角矩阵,斜角具有相同的值,判断一个矩阵是否是对角矩阵。原题
1 | Input: |
方法一:嵌套循环。
1 | def isToeplitzMatrix(self, matrix: 'List[List[int]]') -> 'bool': |
1 | def isToeplitzMatrix(self, matrix: 'List[List[int]]') -> 'bool': |
830. Positions of Large Groups
根据指定字符串分组,超过三个元素的称为大组,求所有大组的位置。原题
1 | Input: "abcdddeeeeaabbbcd" |
方法一:groupby.
1 | def largeGroupPositions(self, S: 'str') -> 'List[List[int]]': |
方法二:two pointers.
1 | def largeGroupPositions(self, S: 'str') -> 'List[List[int]]': |
832. Flipping an Image
水平翻转一张图片并反转(invert). 原题
1 | Input: [[1,1,0],[1,0,1],[0,0,0]] |
1 | def flipAndInvertImage(self, A: 'List[List[int]]') -> 'List[List[int]]': |
840. Magic Squares In Grid
找出grid中数独的个数。原题
1 | Input: [[4,3,8,4], |
方法一:Brute Force.
1 | def numMagicSquaresInside(self, grid: 'List[List[int]]') -> 'int': |
方法二:参考了大神的解法。外层循环中只把左上角的点传入子方法进行判断,并在外循环判断中心点是否为5;
另外一个规律就是,满足条件数独的9宫格中,4个角都是偶数,4个边都是奇数,并且沿着一个方向必然是’43816729’的正序或者倒序。所以当左上角为偶数时,并满足顺序要求,另两个条件也自然满足了。
1 | def numMagicSquaresInside(self, g: 'List[List[int]]') -> 'int': |
849. Maximize Distance to Closest Person
一排电影院座位,离人的最远距离。数组中必含有一个1和0。原题
1 | Input: [1,0,0,0,1,0,1] |
reversed(seats)
也是一个生成器,所以这里要用切片。
1 | def maxDistToClosest(self, seats: 'List[int]') -> 'int': |
方法二:two pointers. 在第一个1出现之前长度都是j。直到第二个1出现时,计算方式变为平均数。这里使用i = j + 1
而不是i = j
是因为有根据i判断的条件,在计算平均距离时又将其加了回来。最后的len(seats)-i
是为了[1, 0, 0, 0]
末尾的0作结算。
1 | def maxDistToClosest(self, seats: 'List[int]') -> 'int': |
1 | def maxDistToClosest(self, seats: 'List[int]') -> 'int': |
867. Transpose Matrix
转置矩阵。原题
1 | Input: [[1,2,3],[4,5,6],[7,8,9]] |
方法一:zip。 这里testcase并没有检测其中的元素是否为list。所以不需要转换。
1 | def transpose(self, A: 'List[List[int]]') -> 'List[List[int]]': |
方法二:zip原理。
1 | def transpose(self, A: 'List[List[int]]') -> 'List[List[int]]': |
方法三:列表生成式。
1 | def transpose(self, A: 'List[List[int]]') -> 'List[List[int]]': |
方法四:numpy.
1 | def transpose(self, A): |
888. Fair Candy Swap
公平的糖果交换。两个人有很多糖果酒吧,交换其中一间使,糖果相同,可能有多个答案,返回任意一个。原题
1 | Input: A = [1,1], B = [2,2] |
方法一:Solution中的答案。
1 | def fairCandySwap(self, A: 'List[int]', B: 'List[int]') -> 'List[int]': |
896. Monotonic Array
判断一个数组是不是单调递增或递减。原题
1 | Input: [1,2,2,3] |
1 | def isMonotonic(self, A: 'List[int]') -> 'bool': |
方法二:迭代一次。
1 | def isMonotonic(self, A: 'List[int]') -> 'bool': |
方法三:python2的一种写法。
1 | def isMonotonic(self, A): |
977. Squares of a Sorted Array
求一个有序数组,平方后的有序结果。原题
1 | Input: [-4,-1,0,3,10] |
方法一:双指针填充数组。
1 | def sortedSquares(self, A: 'List[int]') -> 'List[int]': |
941. Valid Mountain Array
验证是否是山峰数组,前段单调增(不重复),后段单调减,必须是单峰值。原题
1 | Input: [0,3,2,1] |
方法一:传统迭代方式。
1 | def validMountainArray(self, A: 'List[int]') -> 'bool': |
1 | def validMountainArray(self, A: 'List[int]') -> 'bool': |
1 | def validMountainArray(self, A: 'List[int]') -> 'bool': |
845. Longest Mountain in Array
数组中最长的山峰。题和字符串篇821一样的解法。
1 | Input: [2,1,4,7,3,2,5] |
方法一:左右各遍历一次。此题有很多相似题都有这个解法。53 Maximum Subarray,121 Best Time to Buy and Sell Stock,152 Maximum Product Subarray,238 Product of Array Except Self,739 Daily Temperatures,769 Max Chunks to Make Sorted,770 Max Chunks to Make Sorted II,821 Shortest Distance to a Character,845 Longest Mountain in Array
1 | def longestMountain(self, A: List[int]) -> int: |
1 | def longestMountain(self, A: List[int]) -> int: |
942. DI String Match
根据一个”IDID”字符串(I表示增加,D表示减少)生成一个数组,数组元素不能重复,答案不唯一。原题
1 | Input: "IDID" |
1 | def diStringMatch(self, S: 'str') -> 'List[int]': |
1007. Minimum Domino Rotations For Equal Row
旋转最小次,是上下的多米诺骨牌有一行全部相同。原题
方法一:竞赛时写的Brute Force.当时觉得炒鸡硬核。
1 | def minDominoRotations(self, A: List[int], B: List[int]) -> int: |
1 | def minDominoRotations(self, A: List[int], B: List[int]) -> int: |
48. Rotate Image
矩阵顺时针旋转90度。原题
1 | Given input matrix = |
方法一:使用zip。
1 | def rotate(self, matrix: List[List[int]]) -> None: |
方法二:通用写法。
1 | def rotate(self, matrix: List[List[int]]) -> None: |
方法三:找到四个点,直接互换。
1 | def rotate(self, A: List[List[int]]) -> None: |
1020. Partition Array Into Three Parts With Equal Sum
一个数组是否可以分成三个和相同的部分。原题
1 | Input: [0,2,1,-6,6,-7,9,1,2,0,1] |
方法一:竞赛时写的Time: O(n²)的方法。
1 | def canThreePartsEqualSum(self, A: List[int]) -> bool: |
1 | def canThreePartsEqualSum(self, A: List[int]) -> bool: |
1021. Best Sightseeing Pair
得分最高的两个景点。原题
两个景点之间有距离。
1 | Input: [8,1,5,2,6] |
方法一:cur
保存着一个上一次最好的景点。
1 | def maxScoreSightseeingPair(self, A: List[int]) -> int: |
56. Merge Intervals
合并时间段。如果时间段有重叠,则将其合并成一个。原题
1 | Input: [[1,3],[2,6],[8,10],[15,18]] |
方法一:先排序,在根据条件判断是合并还是修改。
1 | # class Interval: |
57. Insert Interval
有一个排好序的时间段,并且没有重复,现在有一个新的段,要求插入其中,如果有重合需要合并。原题
1 | Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] |
方法一:用56的方法合并,使用二分法将其插入。140ms。这题本来想用二分法找到索引,然后前后切片做,后来发现边界太多不好判断,还是从头到尾遍历一遍比较稳。
1 | def insert(self, inter: List[List[int]], newInterval: List[int]) -> List[List[int]]: |
1 | def insert(self, inter: List[List[int]], newInterval: List[int]) -> List[List[int]]: |
1 | def insert(self, inter: List[List[int]], newInterval: List[int]) -> List[List[int]]: |
1030. Matrix Cells in Distance Order
矩阵坐标距离指定点的排序。原题
1 | Input: R = 2, C = 2, r0 = 0, c0 = 1 |
1 | def allCellsDistOrder(self, R: int, C: int, r0: int, c0: int) -> List[List[int]]: |
1033. Moving Stones Until Consecutive
三个石子移到连续的位置,最少和最多需要几步。原题
1 | Input: a = 1, b = 2, c = 5 |
1 | def numMovesStones(self, a: int, b: int, c: int) -> List[int]: |
1051. Height Checker
高度检查。原题
1 | def heightChecker(self, heights: List[int]) -> int: |
1052. Grumpy Bookstore Owner
这题描述的比较抽象,其实就是一个滑动窗口的问题。原题
方法一:一开始我用的双端队列,并且使用了compress.
1 | def maxSatisfied(self, customers: List[int], grumpy: List[int], X: int) -> int: |
1 | def maxSatisfied(self, customers: List[int], grumpy: List[int], X: int) -> int: |
1139. Largest 1-Bordered Square
最大的以1为边长的正方形。原题
1 | Input: grid = [[1,1,1],[1,0,1],[1,1,1]] |
方法一:开始以为像其他小岛问题那样,要回溯延伸。其实此题是暴力法。先对数组进行一个预处理,判断每个点上面和左面连续的1的个数。
1 | def largest1BorderedSquare(self, g: List[List[int]]) -> int: |
1078. Occurrences After Bigram
打印第三个单词。原题
1 | Input: text = "alice is a good girl she is a good student", first = "a", second = "good" |
1 | def findOcurrences(self, text: str, first: str, second: str) -> List[str]: |
1144. Decrease Elements To Make Array Zigzag
每次操作某个元素-1,多少次操作后,可以使数组称为一个zigzag。原题
1 | Input: nums = [9,6,1,6,2] |
方法一:暴力的方法。遍历两次。
1 | def movesToMakeZigzag(self, nums: List[int]) -> int: |
1 | def movesToMakeZigzag(self, nums: List[int]) -> int: |
1089. Duplicate Zeros
复制数组中的0,数组长度不变,多出的元素将被“挤掉”。要求在原数组上修改。原题
1 | Input: [1,0,2,3,0,4,5,0] |
方法一:竞赛时的方法,需要注意末尾不要多加0。空间复杂度过高。
1 | def duplicateZeros(self, arr: List[int]) -> None: |
1 | def duplicateZeros(self, arr: List[int]) -> None: |
1169. Invalid Transactions
非法交易,单笔金额大于1000,或者60分钟内有异地交易,那么为非法交易。列出所有的非法交易。原题
1 | Input: transactions = ["alice,20,800,mtv","alice,50,100,beijing"] |
方法一:暴力法。
1 | def invalidTransactions(self, transactions: List[str]) -> List[str]: |
1170. Compare Strings by Frequency of the Smallest Character
比较最小字符的频率。原题
1 | Input: queries = ["cbd"], words = ["zaaaz"] |
方法一:暴力法。
1 | def numSmallerByFrequency(self, queries: List[str], words: List[str]) -> List[int]: |
1 | def numSmallerByFrequency(self, queries: List[str], words: List[str]) -> List[int]: |
1122. Relative Sort Array
按照数组2的相对位置给另一个数组排序。原题
方法一:和Lee神写法不谋而合,只不过自己用了乘法,其实加法就可以了。
1 | def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: |
1176. Diet Plan Performance
燃烧你的卡路里,卡路里和体重的关系。原题
1 | Input: calories = [1,2,3,4,5], k = 1, lower = 3, upper = 3 |
1 | def dietPlanPerformance(self, calories: List[int], k: int, lower: int, upper: int) -> int: |
1395. Count Number of Teams
数字分组计数。原题
1 | Input: rating = [2,5,3,4,1] |
1 | def numTeams(self, rating: List[int]) -> int: |
1375. Bulb Switcher III
灯泡开关,记录灯泡全部变蓝的次数。原题
1 | Input: light = [2,1,3,5,4] |
方法一:使用堆。其实没必要增加O(n)的空间
1 | def numTimesAllBlue(self, light: List[int]) -> int: |
1 | def numTimesAllBlue(self, light: List[int]) -> int: |
1 | def numTimesAllBlue(self, light: List[int]) -> int: |
498. Diagonal Traverse
对角线z字形遍历。原题
方法一:费劲心思去找i,j的关系,其实只需要知道一点,i和j都是越来越大的就行。
1 | def findDiagonalOrder(self, matrix: List[List[int]]) -> List[int]: |
1424. Diagonal Traverse II
对角线遍历,每行长度可能不一样。原题
1 | def findDiagonalOrder(self, g: List[List[int]]) -> List[int]: |
1365. How Many Numbers Are Smaller Than the Current Number
数组中比当前数小的个数。原题
方法一:排序。T=O(n*lgn)
1 | def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]: |
方法二:利用了数的范围在1~100之间。
1 | def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]: |
1366. Rank Teams by Votes
投票选举。首先按照排名,然后按照字母顺序。原题
1 | Input: votes = ["ABC","ACB","ABC","ACB","ACB"] |
方法一:列举了所有的票数,然后对tuple进行排序。
1 | def rankTeams(self, votes: List[str]) -> str: |
1 | def rankTeams(self, votes: List[str]) -> str: |
1331. Rank Transform of an Array
将数组转化为排行。原题
1 | Input: arr = [40,10,20,30] |
方法一:setdefault
的妙用。 by Lee215
1 | def arrayRankTransform(self, arr: List[int]) -> List[int]: |
1306. Jump Game III
跳跃游戏,从start开始,可以沿着value向前向后跳,求是否可以跳到0.原题
1 | Input: arr = [4,2,3,0,3,1,2], start = 5 |
方法一:Bfs.
1 | def canReach(self, arr: List[int], start: int) -> bool: |
方法二:数组中元素均为非负数,所以用负数来标记已经跳过的点。
1 | def canReach(self, arr: List[int], i: int) -> bool: |
1696. Jump Game VI
跳跃游戏,从数组开头跳跃到数组末尾,数组中元素可能包含负数,每次跳的距离为1~k,跳到哪里可以获得对应的分,问跳到最后最多能得多少分。
1 | Input: nums = [1,-1,-2,4,-7,3], k = 2 |
方法一:比赛时只想到了O(n^2)的dp方法,啥也不是。这题用单调队里,维护一个最长为k的队列,队列中记录索引,有点像剑指offer中滑动窗口的最大值。
1 | def maxResult(self, nums: List[int], k: int) -> int: |
1297. Maximum Number of Occurrences of a Substring
出现次数最多的子串。滑动窗口可伸缩,最多包含maxLetters个字母。原题
1 | def maxFreq(self, s: str, maxLetters: int, k: int, maxSize: int) -> int: |
1291. Sequential Digits
按序求组区间中的顺子。原题
1 | Input: low = 1000, high = 13000 |
方法一:开始时用的转化字符串的方式。不优雅,借鉴了他人解法。生成器有个好处是你可以暂时先不关心顺序。因为low>=10,所以一开始不会以9开头。
1 | def sequentialDigits(self, low: int, high: int) -> List[int]: |
1275. Find Winner on a Tic Tac Toe Game
三子棋的游戏,谁先连到3个子谁就赢。原题
1 | Input: moves = [[0,0],[2,0],[1,1],[2,1],[2,2]] |
方法一:这题是为数不多easy里面想了时间那么长的,除了暴力法没有想到什么思路。
1 | def tictactoe(self, moves: List[List[int]]) -> str: |
1 | def tictactoe(self, moves: List[List[int]]) -> str: |
面试题 16.04. 井字游戏
比1275简单一点,给定棋盘,判断谁赢。
方法一:自己用if写的,评论有个正则的写法不错,学习一下。
1 | def tictactoe(self, board: List[str]) -> str: |
1260. Shift 2D Grid
2D滑动,每次列右移一次,首列下移一次。原题
1 | Input: grid = [[1,2,3],[4,5,6],[7,8,9]], k = 1 |
方法一:deque。最直观的方法。
1 | def shiftGrid(self, g: List[List[int]], k: int) -> List[List[int]]: |
方法二:调了半天,找了一些规律,然而并没有方法一快多少,时间上差不多,反而是更难理解了。
1 | def shiftGrid(self, g: List[List[int]], k: int) -> List[List[int]]: |
1 | def shiftGrid(self, g: List[List[int]], k: int) -> List[List[int]]: |
1252. Cells with Odd Values in a Matrix
根据坐标每次将所在的行和列+1,这个点+2,统计所有的奇数个数。原题
1 | Input: n = 2, m = 3, indices = [[0,1],[1,1]] |
方法一:异或。
1 | def oddCells(self, n: int, m: int, indices: List[List[int]]) -> int: |
1248. Count Number of Nice Subarrays
找到包含k个奇数的子数组个数。原题
1 | Input: nums = [1,1,2,1,1], k = 3 |
方法一:找到所有的奇数索引,然后滑动窗口累加值。
1 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: |
方法二:遍历一次
1 | def numberOfSubarrays(self, A: List[int], k: int) -> int: |
1234. Replace the Substring for Balanced String
将每个QWER出现的次数变为一样,最少需要修改的子串长度是多少。原题
1 | Input: s = "QQWE" |
方法一:例子有地不好,子串必须是连续的。和lee大佬解法差不多。
1 | def balancedString(self, s: str) -> int: |
1208. Get Equal Substrings Within Budget
将字符串s变为t。需要ascii码的差,问在指定的差中,最长的可以变成的子串长度是多少。原题
1 | Input: s = "abcd", t = "bcdf", maxCost = 3 |
方法一:滑动窗口问题。比赛的答案。
1 | def equalSubstring(self, s: str, t: str, maxCost: int) -> int: |
方法二:Lee215,开始比较迷惑为什么i, j 不用max来求值,看了评论发现有人和我有一样的疑惑,并且给了解释,对于此题而言,滑动窗口的长度不会缩短。因为只用了if 而不是while
1 | def equalSubstring(self, s: str, t: str, cost: int) -> int: |
Sort Colors
0,1,2的数组排序。原题
1 | Input: [2,0,2,1,1,0] |
方法一:没想到有快排的思想。维持三个区间[0,i) [i,j),[j,k)
分别表示0,1,2的区间。
1 | def sortColors(self, nums: List[int]) -> None: |
1191. K-Concatenation Maximum Sum
求k*arr的连续数组最大和。原题
1 | Input: arr = [1,2], k = 3 |
方法一:想到了卡登算法,但是没想明白为啥要加(k-2)个数组的和,因为首位数组中间可以夹带(k-2)个数组,如果数组和是正数的话,就将它算进去
1 | def kConcatenationMaxSum(self, arr: List[int], k: int, mod=10**9+7) -> int: |
448. Find All Numbers Disappeared in an Array
找出n长度的数组中1-n缺失的数字,有的数字会出现多次。原题
1 | Input: |
方法一:这道题解法挺新颖,看着这题和剑指offer中的有点类似,但是那道题是其它数字出现一次。这个题的解法是出现的位置的数变成负的,最后找正数的索引。
1 | def findDisappearedNumbers(self, nums: List[int]) -> List[int]: |
11. Container With Most Water
最大的水容积,用坐标轴装水,有点像木桶原理。原题
方法一:双指针,开始没有想到指针如何移动。其实这样想,如果一个坐标比较矮,那就将其舍弃,因为宽度是越来越小的,所以需要更高的木桶才能弥补。
def maxArea(self, height: List[int]) -> int:
lo, hi = 0, len(height)-1
ans = 0
while lo < hi:
ans = max(ans, (hi-lo)*min(height[hi], height[lo]))
if height[lo] < height[hi]:
lo += 1
else:
hi -= 1
return ans
1498. Number of Subsequences That Satisfy the Given Sum Condition
给定一个数组,返回最大值最小值和小于目标值的子序列,可以不连续,的个数。原题
1 | Input: nums = [3,5,6,7], target = 9 |
方法一:一开始思路相对了,但是指针移动没想好。来自Lee215,累加的时候要计算一下mod否则效率会变很慢。
1 | def numSubseq(self, nums: List[int], target: int) -> int: |
463. Island Perimeter
小岛的周长,小岛中间没有湖。原题
1 | Input: |
方法一:stefan、因为中间没有湖,所以呢周长等于所以相邻格子不相等的个数。这里将列也放在一起计算了
1 | def islandPerimeter(self, grid: List[List[int]]) -> int: |
15. 3Sum
找出数组中3个数相加为0,返回所有的组合非重复。原题
1 | Given array nums = [-1, 0, 1, 2, -1, -4], |
方法一:这里看了提示后用的2sum的方法。
1 | def threeSum(self, nums: List[int]) -> List[List[int]]: |
方法二:讨论区看到的一个方法。明白了还有许多可以优化的地方。
1 | def threeSum(self, nums: List[int]) -> List[List[int]]: |
1508. Range Sum of Sorted Subarray Sums
数组的累加和排序,取区间中的数字和。原题
1 | Input: nums = [1,2,3,4], n = 4, left = 1, right = 5 |
方法一:O(n^2)的方法。比赛的时候还用了accumulate, 但是其实没必要。
1 | def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int: |
1509. Minimum Difference Between Largest and Smallest Value in Three Moves
删除3个数,数组的最大值最小值差最小是多少。原题
方法一:首次ac的方法。思路很直观,但是写法却有点复杂。
1 | def minDifference(self, nums: List[int]) -> int: |
1 | def minDifference(self, nums: List[int]) -> int: |
1 | def minDifference(self, nums: List[int]) -> int: |
1529. Bulb Switcher IV
灯泡开关,每次只能开关从后开始的连续的n个,问变成目标状态,最少需要几次操作。原题
1 | Input: target = "10111" |
方法一:竞赛时的方法。 分组。
1 | def minFlips(self, target: str) -> int: |
1 | def minFlips(self, target: str) -> int: |
1 | def minFlips(self, target: str) -> int: |
442. Find All Duplicates in an Array
找到所有重复的元素,数组中的元素都在1~n之间,n为数组的长度。原题
方法一:要求在O(n)时间,O(1)空间实现,那么就考虑修改原数组来节省空间。以负值来记录是否出现过。
1 | def findDuplicates(self, nums: List[int]) -> List[int]: |
713. Subarray Product Less Than K
连续子数组乘积小于k的个数,元素为正数。原题
1 | Input: nums = [10, 5, 2, 6], k = 100 |
方法一:一开始想到了双端队列,但是累加时end-start+1数量没想到。此题用双指针即可
1 | def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: |
915. Partition Array into Disjoint Intervals
将数组拆成两个非空数组,要求左侧的每个元素都小于等于右侧的每个元素,求左侧最小的长度,假设答案一定存在。原题
1 | Input: [5,0,3,8,6] |
方法一:蛮简单的,two-pass的方法,这个累加函数的生成器不能直接reversed,要将其转换成数组,这里产生了一次遍历。
1 | def partitionDisjoint(self, A: List[int]) -> int: |
1 | def partitionDisjoint(self, A: List[int]) -> int: |
1562. Find Latest Group of Size M
将一个全是0的字符串按照arr的索引顺序变为1,整体被0分割成若干段,问最后有m个长度的”1”的段时是第几步、原题
1 | Input: arr = [3,5,1,2,4], m = 1 |
方法一:比赛时没做出来,Lee的方法。length表示第i个bit的长度,count表示这么长的group有多少个。严格来说length[a-left]
和lenght[a+right]
区间内都应该变成left+right+1。但是由于中间的后续用不到,所以不必赋值。
1 | def findLatestStep(self, arr: List[int], m: int) -> int: |
334. Increasing Triplet Subsequence
数组中是否有三个元素递增。原题
1 | Input: [1,2,3,4,5] |
方法一:首次ac的方法,看了要求在O(n)时间O(1)空间实现。
1 | def increasingTriplet(self, nums: List[int]) -> bool: |
1 | inc = [float('inf')] * 2 |
926. Flip String to Monotone Increasing
将一个二进制字符串翻转成单调递增最少要几步。原题
1 | Input: "010110" |
方法一:看了一眼讨论区,就明白了,遍历时找到递增的点,也就是第一个1,记录后边的0和前边的1。这里我是在后面补1,也可以将ans初始化为len(S)-suffix_0。
1 | def minFlipsMonoIncr(self, S: str) -> int: |
424. Longest Repeating Character Replacement
由26个大写字母组成的字符串,在k次替换范围内,产生的最长的重复字符串是多少。原题
1 | Input: |
方法一:滑动窗口加数组计数。time- O(26N)。while是没有必要的,left+1后 左边等号就会刚好=k, 不过这里的left, right表示的区间意义变了,表示的是最大的滑动窗口,窗口内的字符串不一定是可以满足条件的。
1 | def characterReplacement(self, s: str, k: int) -> int: |
方法二:研究了很久也没弄明白,为什么maxf曾经最大的值,可以替代max(cnt)。理论上时间确实比上述快了。Time-O(N)。我试了一个特殊的例子,”BBBCADEF”1,在某些情况maxf > max(cnt)的,但即便这样也没有影响if判断,猜测可能为right-left+1区间为最大区间。不过又将if改成while循环,依然没有影响。此解法还是有些疑惑。
1 | def characterReplacement(self, s: str, k: int) -> int: |
398. Random Pick Index
有这样一个数组,我想随机的去一个目标的元素,找到和目标相等的这些元素随机返回一个索引。原题
1 | int[] nums = new int[] {1,2,3,3,3}; |
方法一:直接放到defaultdict中也没有超过空间限制。
1 | class Solution: |
方法二:一个新的方法叫作蓄水池取样,当遇见了一个目标数,就将它放到池子中,然后随机一个数。并在随到当前数时更新索引。
1 | class Solution: |
382. Linked List Random Node
在一个链表上随机取一个节点值。原题
方法一:和398一样。假设链表无限大,不能够获取它的长度。这是非常经典的一个题。一个很大的数据流,对数据流的内容只能访问一次,随机算法使数据流中所有的被选中的概率相等。
1 | class Solution: |
209. Minimum Size Subarray Sum
累加和大于s的最短的子数组长度。原题
1 | Input: s = 7, nums = [2,3,1,2,4,3] |
方法一:滑动窗口。注意A为空的情况。
1 | def minSubArrayLen(self, s: int, A: List[int]) -> int: |
930. Binary Subarrays With Sum
求和我S的子数组个数,数组元素只包含0,1。原题
1 | Input: A = [1,0,1,0,1], S = 2 |
方法一:这种题要求at_most. 滑动窗口,用和小于等于S的子数组个数减去小于等于S-1的子数组个数。
1 | def numSubarraysWithSum(self, A: List[int], S: int) -> int: |
969. Pancake Sorting
煎饼排序。由1~n组成,没次只能reverse前k个,求k的数组,答案不唯一。原题
1 | Input: A = [3,2,4,1] |
方法一:例子中的做法不是很好,只需要每次找最大的,然后翻到首位,然后再全翻转使其到达末尾。
1 | def pancakeSort(self, A: List[int]) -> List[int]: |
1 | def pancakeSort(self, A: List[int]) -> List[int]: |
1566. Detect Pattern of Length M Repeated K or More Times
判断数组中是否有k次以上个重复的M大小的组。原题
1 | Input: arr = [1,2,4,4,4,4], m = 1, k = 3 |
方法一:暴力。
1 | def containsPattern(self, arr: List[int], m: int, k: int) -> bool: |
(k-1)*m
因为第一个用来比较,不算在内,如果在达到之前有一个不相等,则归零。
1 | def containsPattern(self, arr: List[int], m: int, k: int) -> bool: |
228. Summary Ranges
格式化一段range。原题
1 | Input: [0,1,2,4,5,7] |
方法一:很简单,记录这题主要是学到了一个新的写法。先贴自己的解法。
1 | def summaryRanges(self, nums: List[int]) -> List[str]: |
方法二:stefan的写法。[][1:] = 1,
数组会变成[1]。
1 | def summaryRanges(self, nums: List[int]) -> List[str]: |
769. Max Chunks To Make Sorted
可以将一个数组分成几块小数组,然后使每个小数组排序后连接起来,整个大数组有序,问最多可以分成多少块。原题
1 | Input: arr = [1,0,2,3,4] |
方法一:直白来看,如果某一段包含了排序后应该有的所有的数,那么久将其分成一段。
1 | def maxChunksToSorted(self, arr: List[int]) -> int: |
1 | def maxChunksToSorted(self, arr: List[int]) -> int: |
390. Elimination Game
消除游戏,从1~n的数组,每次从左到右隔一个删除,然后从右到左隔一个删除。最后剩下一个数字是哪个。原题
1 | Input: |
方法一:在用切片方法发现n能到1亿时,超时了;然后想到其实只需要控制一个范围即可。每次操作后,数组中的等差变为原来的2倍。
1 | def lastRemaining(self, n: int) -> int: |
1 | def lastRemaining(self, n: int) -> int: |
853. Car Fleet
超车车队,说有这么一些车向同一个目的地出发,起始位置和速度不同,当快车遇见慢车时不能超过,而是跟在后面变成一个车队。问最后到达终点时有几个车队,1辆车也算一个车队。原题
1 | Input: target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3] |
方法一:思路很快想出来了,就是排序,但是根据什么排序,怎么比较想了半天。问题出在这个例子上:10, [0,4,2],[2,1,3]
这个排序后时[(4, 6), (2, 2.6), (0, 5)]
,以起始点位置排序,当一个时间小于等于之前的时间时,那么这辆车就能追上之前的,变成一个车队;反之,则形成一个单独的车队。这个“之前的时间”指的不是挨着的前面的一个时间,而是之前最慢的一个车。也就是时间最大的。
1 | def carFleet(self, target: int, position: List[int], speed: List[int]) -> int: |
1574. Shortest Subarray to be Removed to Make Array Sorted
删除一个最短的子数组使整个数组有序。问最短数组长度为多少。原题
1 | Input: arr = [1,2,3,10,4,2,3,5] |
方法一:这个题竞赛时没做出来,只想到从左到右找坏的点,没想到从右到左也需要找一次。而且找完之后,要控制两个指针,从0和j出发遍历,自己想的是从中间往两边遍历。边界条件非常多。
1 | def findLengthOfShortestSubarray(self, arr: List[int]) -> int: |
835. Image Overlap
图片覆盖。两个二维矩阵,A可以向一个方向滑动N个单位,然后放到B上,如果都为1,则表示某个像素是覆盖的。求最多有多少个像素覆盖。原题
1 | Input: A = [[1,1,0], |
方法一:逆向思维,将1的点都求出来,然后每两个做比较。相同偏移量的算到一起。
def largestOverlap(self, A: List[List[int]], B: List[List[int]]) -> int:
A = [(x, y) for x, row in enumerate(A) for y, d in enumerate(row) if d]
B = [(x, y) for x, row in enumerate(B) for y, d in enumerate(row) if d]
c = Counter((xa-xb, ya-yb) for xa, ya in A for xb, yb in B)
return max(c.values() or [0])
方法二:数学的降维打击。卷积。其中涉及到了一些数学知识还没有完全参透。
1 | import numpy as np |
1 | from scipy.signal import correlate2d |
42. Trapping Rain Water
接雨水。给定一些柱状图的高度,问能接多少雨水。原题
方法一:用了逆向思维,通过总面积-损失的水-柱体面积求的。
1 | def trap(self, height: List[int]) -> int: |
1 | def trap(self, height: List[int]) -> int: |
36. Valid Sudoku
验证一个数独的正确性,只需要考虑填入数字的格子。原题
方法一:比较直观的写法。
1 | def isValidSudoku(self, board: List[List[str]]) -> bool: |
dict_values
的对象。
1 | def isValidSudoku(self, board: List[List[str]]) -> bool: |
1 | def isValidSudoku(self, board: List[List[str]]) -> bool: |
1054. Distant Barcodes
分散的条形码,就是将一个数组中的元素重新排序,让其没有两个一样的相邻。原题
方法一:我首次AC的方法就是用堆,取出一个或者2个数。
1 | def rearrangeBarcodes(self, barcodes: List[int]) -> List[int]: |
1 | def rearrangeBarcodes(self, barcodes: List[int]) -> List[int]: |
1 | def rearrangeBarcodes(self, a: List[int]) -> List[int]: |
1588. Sum of All Odd Length Subarrays
求所有奇数长度的子数组的和。
方法一:这题给的范围比较小,竞赛时用O(n^2)暴力就解了,不过实际有O(n)的方法。通过前缀和的方式,累加,再通过减法算和。比如[1,4,2,5,3]
。j-i
表示的是子数组的长度。
1 | def sumOddLengthSubarrays(self, arr: List[int]) -> int: |
1 | 0 1 1 0 |
方法二:通过观察可以找到规律,每个数字出现的个数是有规律的。
1 | 1 2 3 4 5 subarray length 1 |
对于第i个元素,包含第i个元素的子数组=`(i+1) (n-i)
奇数数组
(x+1)//2`, x表示总数。
1 | def sumOddLengthSubarrays(self, arr: List[int]) -> int: |
面试题 16.22. 兰顿蚂蚁
兰顿蚂蚁(英语:Langton’s ant)是细胞自动机的例子。它由克里斯托夫·兰顿在1986年提出,它由黑白格子和一只“蚂蚁”构成[1],是一个二维图灵机。兰顿蚂蚁拥有非常简单的逻辑和复杂的表现。在2000年兰顿蚂蚁的图灵完备性被证明。兰顿蚂蚁的想法后来被推广,比如使用多种颜色。
若从全白的背景开始,在一开始的数百步,蚂蚁留下的路线会出现许多对称或重复的形状,然后会出现类似混沌的假随机,至约一万步后会出现以104步为周期无限重复的“高速公路”朝固定方向移动[2]。在目前试过的所有起始状态,蚂蚁的路线最终都会变成高速公路,但尚无法证明这是无论任何起始状态都会导致的必然结果[3]。
这道题非常有意思。一只蚂蚁坐在由白色和黑色方格构成的无限网格上。开始时,网格全白,蚂蚁面向右侧。每行走一步,蚂蚁执行以下操作。返回能够包含蚂蚁走过的所有方格的最小矩形。
(1) 如果在白色方格上,则翻转方格的颜色,向右(顺时针)转 90 度,并向前移动一个单位。
(2) 如果在黑色方格上,则翻转方格的颜色,向左(逆时针方向)转 90 度,并向前移动一个单位
1 | def printKMoves(self, K: int) -> List[str]: |
方法二:受评论区大神启发,使用defaultdict
来避免每次都移动数组。写法是不错,但是时间和空间上都比方法一差太多,时间是3,4倍,空间是10倍。所以我改了一下改成两个defaultdict,时间上虽然还是不如方法一,但是时间空间都好了一半。最后是1500ms, 240M。
1 | def printKMoves(self, K: int) -> List[str]: |
41. First Missing Positive
找到数组中最小的缺失的正数。数组中可能包含负数。要求在O(N)时间,常数空间实现。
方法一:难点在于复杂度的要求。这个方法没想到,但是感觉之前用过,忘记是哪道题了。就是将元素放在它对应的索引上。
1 | def firstMissingPositive(self, nums: List[int]) -> int: |
面试题 17.18. 最短超串
找到包含small所有元素最短子数组的最小索引。
1 | big = [7,5,9,0,2,1,3,5,7,9,1,1,5,8,8,9,7] |
方法一:滑动窗口。挺简单的一次就AC了。
1 | def shortestSeq(self, big: List[int], small: List[int]) -> List[int]: |
289. Game of Life
生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
根据当前状态,写一个函数来计算面板上所有细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
方法一:首次AC的方法。
1 | def gameOfLife(self, board: List[List[int]]) -> None: |
方法二:如果面板是无限的,如何考虑呢?Stefan这样写,一个辅助函数来根据所有当前活着的细胞计算下一个状态的活细胞集。
1 | def gameOfLife(self, board: List[List[int]]) -> None: |
方法三:此题可以使用生成器来建模,在《effictive python》中有一个非常经典的例子,《Fluent Python》中作者也提到了这个例子。我在次基础上修改了一点,主要是针对边界处理。每一个细胞都表示为一个协程,并令这些协程步调一致地向前推进。step_cell
是一个协程,会生成·Transition
对象用来表示细胞的状态迁移。每个细胞都可以通过运行step_cell
来迁移到下一个状态。待所有细胞都迁移好之后,游戏的始终就会向前走一步。只要simulate
协程在推进,这个过程就会一直持续下去。协程的优势就在于此。它令开发者所用的实现代码相互解耦。这使得程序好像能够平行地运行多个协程,也使得开发者能够在不修改协程的前提下,逐渐改进发布指令时所用的代码。不过书中说的一点没有明白”如果传入的坐标越界,那就自动折回,这使得网格看上去好像是一种无限循环的空间”,书中使用了取余的方式,但是本题中越界应该返回0。这套模板代码演示了如何用协程分离程序中的各个关注点,而关注点的分离,正是一条重要的设计原则。
1 | Query = namedtuple('Query', 'y x') # 用于查询细胞的状态,这里实现很巧妙可以使协程通过此对象向外围环境查询信息 |
164. Maximum Gap
在线性时间空间复杂度找到数组中两个排好序后的相邻元素的最大差。
1 | Input: [3,6,9,1] |
方法一:桶排序。 如果将这些数平分,差最小也是size,所以最大值会出现在两个相邻的桶之间,而不会在一个桶内。这里同时记录了最小值和最大值。
1 | def maximumGap(self, nums: List[int]) -> int: |
1755. Closest Subsequence Sum
找到最接近目标值的子序列。
1 | Input: nums = [5,-7,3,5], goal = 6 |
方法一:分治+dfs。这题主要先想到分治,然后才能做,因为数组长度是40.直接做会超时。
1 | def minAbsDifference(self, nums: List[int], goal: int) -> int: |
992. Subarrays with K Different Integers
k个不同数字的连续子数组的个数。
1 | Input: A = [1,2,1,2,3], K = 2 |
方法一:刚好K个等于最少k个-最少k-1个。滑动窗口。
1 | def subarraysWithKDistinct(self, A: List[int], K: int) -> int: |
995. Minimum Number of K Consecutive Bit Flips
每次翻转连续k个数字,能否全部翻转成1.需要多少次。
1 | Input: A = [0,0,0,1,0,1,1,0], K = 3 |
方法一:贪心+滑动窗口。如果模拟30000的长度会超时。使用滑动窗口来做,看前k个队列中翻转的奇偶性,判断当前数是否翻转。如果翻转长度超过数组长度,则无法完成翻转。
1 | def minKBitFlips(self, A: List[int], K: int) -> int: |
395. Longest Substring with At Least K Repeating Characters
找到字符串中最长子串,要求每个字符频率不少于K,返回最长的子串长度。
1 | 输入:s = "aaabb", k = 3 |
方法一:这题,滑动窗口不好滑,递归思想简单。
1 | def longestSubstring(self, s: str, k: int) -> int: |
1838. Frequency of the Most Frequent Element
有k次机会可以将数组中的元素加1,问能得到数组中最大的频数是多少。
1 | Input: nums = [1,2,4], k = 5 |
方法一:比赛的时候,滑动窗口想了一下,没找到条件。遗憾没做出来。因为每次都是+1, 所以条件是k+sum >= size * max
1 | def maxFrequency(self, nums: List[int], k: int) -> int: |
2106. Maximum Fruits Harvested After at Most K Steps
最多走k步,可以向左或向右走,最多能收集多少个草莓。
1 | Input: fruits = [[2,8],[6,3],[8,6]], startPos = 5, k = 4 |
方法一:比赛过后写出来的,比赛的方法写得过于复杂。其实简单的前缀和就好。需要注意的是往返需要2倍的步数,可以向左或向右往返。
1 | def maxTotalFruits(self, fruits: List[List[int]], startPos: int, k: int) -> int: |
862. Shortest Subarray with Sum at Least K
给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1 。
1 | 输入:nums = [2,-1,2], k = 3 |
方法一:前缀和很容易想到。之后需要维护一个队列,左侧删除也比较容易想到。右侧删除有点难想。
1 | def shortestSubarray(self, nums: List[int], k: int) -> int: |
2448. Minimum Cost to Make Array Equal
给你两个下标从 0 开始的数组 nums 和 cost ,分别包含 n 个 正 整数。
你可以执行下面操作 任意 次:
将 nums 中 任意 元素增加或者减小 1 。
对第 i 个元素执行一次操作的开销是 cost[i] 。
请你返回使 nums 中所有元素 相等 的 最少 总开销。
1 | 输入:nums = [1,3,5,2], cost = [2,3,1,14] |
方法一:这题竞赛没有过,题解也是看了很久,题目本身并不难。将nums和cost一起排序。首先计算所有等于nums[0]
的开销,以及所有的cost。然后考虑所有的数变成nums[1]
。这样要增加(nums[1]-nums[0]) * cost[0]
,要减少(nums[1]-nums[0])*(sum_cost-cost[0])
。之后考虑所有的数变成nums[2]
,要增加(nums[2]-nums[1])*(cost[0]+cost[1])
,减少(nums[2]-nums[1])*(sum_cost-cost[0]-cost[1])
。总共减少(nums[i+1]-nums[i]) * (sum_cost-cost[0]*2-cost[1]*2...-cost[i]*2)
1 | def minCost(self, nums: List[int], cost: List[int]) -> int: |
805. Split Array With Same Average
给定你一个整数数组 nums
我们要将 nums
数组中的每个元素移动到 A
数组 或者 B
数组中,使得 A
数组和 B
数组不为空,并且 average(A) == average(B)
。
如果可以完成则返回true
, 否则返回 false
。
1 | 输入: nums = [1,2,3,4,5,6,7,8] |
方法一:看了题解才明白,首先要明白转化问题,目标是找到一个子数组,平均数为总数组的平均数。题中数组长度为30,如果遍历将会为2**30,超时,所以可以将问题折半,变为两个数组。分为三种情况:子数组在数组左侧,子数组在数组右侧,子数组左右都有。由于平均数浮点有精度问题,所以我们将所有数乘以N,然后减去sum(nums),这样问题变为,找到一个子数组和为0.
1 | def splitArraySameAverage(self, nums: List[int]) -> bool: |