目录
243. 最短单词距离
766. 托普利茨矩阵
面试题 17.04. 消失的数字
169. 多数元素
1287. 有序数组中出现次数超过25%的元素
119. 杨辉三角 II
面试题 16.17. 连续数列
1170. 比较字符串最小字母出现频次
1089. 复写零
448. 找到所有数组中消失的数字
442. 数组中重复的数据
https://leetcode-cn.com/problems/shortest-word-distance/
给定一个单词列表和两个单词 word1 和 word2,返回列表中这两个单词之间的最短距离。
示例:假设 words = ["practice", "makes", "perfect", "coding", "makes"],输入: word1 = “coding”, word2 = “practice”,输出: 3输入: word1 = "makes", word2 = "coding",输出: 1
注意:你可以假设 word1 不等于 word2, 并且 word1 和 word2 都在列表里。
思路
一:两重循环,查找是否有符合条件的,并更新最短距离,时间复杂度O(kn^2),n是words数组的长度,k是words中单词的最大长度。
二:优化的二重循环,时间复杂度O(nk+n^2),rec_1和rec_2分别记录了word1和word2在words中的下标,然后在俩数组中找到最小的差值即ok了。
class Solution(object):
def shortestDistance(self, words, word1, word2):
"""
:type words: List[str]
:type word1: str
:type word2: str
:rtype: int
"""
rec_1, rec_2 = [], []
for i in range(len(words)):
if words[i] == word1:
rec_1.append(i)
elif words[i] == word2:
rec_2.append(i)
res = abs(rec_1[0] - rec_2[0])
for i in rec_1:
for j in rec_2:
res = min(res, abs(i-j))
return res
三:用idx1和idx2分别记录word1和word2的最新出现的位置,每次发现一个新的单词,不必要再循环遍历,因为已经记录过最新的位置,时间复杂度O(kn)。
class Solution(object):
def shortestDistance(self, words, word1, word2):
res, idx1, idx2 = len(words), -1, -1
for i in range(len(words)):
if words[i] == word1:
idx1 = i
elif words[i] == word2:
idx2 = i
if idx1 != -1 and idx2!= -1:
res = min(res, abs(idx1 - idx2))
return res
https://leetcode-cn.com/problems/toeplitz-matrix/
如果一个矩阵的每一方向由左上到右下的对角线上具有相同元素,那么这个矩阵是托普利茨矩阵。
给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True。
示例 1:输入: matrix = [[1,2,3,4],[5,1,2,3],[9,5,1,2]],输出: True,解释:在上述矩阵中, 其对角线为:"[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]"。各条对角线上的所有元素均相同, 因此答案是True。
示例 2:输入:matrix = [[1,2],[2,2]],输出: False,解释: 对角线"[1, 2]"上的元素不同。
说明: matrix 是一个包含整数的二维数组。matrix 的行数和列数均在 [1, 20]范围内。matrix[i][j] 包含的整数在 [0, 99]范围内。
思路
一:对角线
class Solution(object):
def isToeplitzMatrix(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: bool
"""
if not matrix or not matrix[0]:
return False
m, n = len(matrix), len(matrix[0])
rec = [0] * (m + n - 1)
idx = 0
for i in range(m -1 , 0, -1):
rec[idx] = matrix[i][0]
idx += 1
for j in range(0, n):
rec[idx] = matrix[0][j]
idx += 1
for i in range(m):
for j in range(n):
if matrix[i][j] != rec[j - i + m - 1]:
return False
return True
二:检查左上邻居。
https://leetcode-cn.com/problems/missing-number-lcci/
数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
注意:本题相对书上原题稍作改动
示例 1:输入:[3,0,1],输出:2
示例 2:输入:[9,6,4,2,3,5,7,0,1],输出:8
思路
一:创建一个n+1个元素的列表,若某值出现,则以该值为下标的元素计1。
class Solution(object):
def missingNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
rec = [0] * (len(nums)+1)
for num in nums:
rec[num] = 1
for i in range(len(rec)):
if rec[i] == 0:
return i
二:copy一下leetcode上大神的解法,https://leetcode-cn.com/problems/missing-number-lcci/solution/onshi-jian-fu-za-du-o1kong-jian-fu-za-du-shi-xian-/,借用异或操作,res = res ^ x ^ x。对同一个值异或两次,那么结果等于它本身,所以我们对res从0-nums.length进行异或,同时对nums数组中的值进行异或,出现重复的会消失,所以最后res的值是只出现一次的数字,也就是nums数组中缺失的那个数字。
class Solution(object):
def missingNumber(self, nums):
res = 0
for i in range(len(nums)):
res ^= i
res ^= nums[i]
res ^= len(nums)
return res
https://leetcode-cn.com/problems/majority-element/
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:输入: [3,2,3],输出: 3
示例 2:输入: [2,2,1,1,1,2,2],输出: 2
思路
一:排序,因为此处众数的定义是出现次数大于 ⌊ n/2 ⌋ 的元素,且保证存在,则该数一定是下标⌊ n/2 ⌋的元素,例如长度为3,则下标1上的元素一定是结果;长度为4,则下标2上的元素一定是结果。
class Solution(object):
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums = sorted(nums)
return nums[len(nums)//2]
二:消消乐。开辟一个列表,列表的第一个元素放候选众数,第二个元素放计数,若遍历的元素与候选众数同,则计数加1,若不同,计数减1;若计数为0,则将该数作为新的候选众数,并计1;最终的结果第一个元素是众数,第二个元素也必然大于0。其实方法一和方法二能用都是因为题目中众数的定义,长度必须占到 ⌊ n/2 ⌋ +1。后面看了一下题解,其实与官方题解的法6一样。https://leetcode-cn.com/problems/majority-element/solution/qiu-zhong-shu-by-leetcode-2/,感觉官方写的挺清晰的,copy一下。如果我们把众数记为 +1+1 ,把其他数记为 −1−1 ,将它们全部加起来,显然和大于 0 ,从结果本身我们可以看出众数比其他数多。本质上,就是找 nums 的一个后缀 suf ,其中 suf[0] 就是后缀中的众数。我们维护一个计数器,如果遇到一个我们目前的候选众数,就将计数器加一,否则减一。只要计数器等于 0 ,我们就将 nums 中之前访问的数字全部 忘记 ,并把下一个数字当做候选的众数。直观上这个算法不是特别明显为何是对的,我们先看下面这个例子(竖线用来划分每次计数器归零的情况)[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 7, 7, 7, 7]首先,下标为 0 的 7 被当做众数的第一个候选。在下标为 5 处,计数器会变回0 。所以下标为 6 的 5 是下一个众数的候选者。由于这个例子中 7 是真正的众数,所以通过忽略掉前面的数字,我们忽略掉了同样多数目的众数和非众数。因此, 7 仍然是剩下数字中的众数。[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 5, 5, 5, 5]现在,众数是 5 (在计数器归零的时候我们把候选从 7 变成了 5)。此时,我们的候选者并不是真正的众数,但是我们在 遗忘 前面的数字的时候,要去掉相同数目的众数和非众数(如果遗忘更多的非众数,会导致计数器变成负数)。因此,上面的过程说明了我们可以放心地遗忘前面的数字,并继续求解剩下数字中的众数。最后,总有一个后缀满足计数器是大于 0 的,此时这个后缀的众数就是整个数组的众数。
class Solution(object):
def majorityElement(self, nums):
rec = [nums[0], 1]
for i in range(1,len(nums)):
if rec[1] == 0:
rec = [nums[i], 1]
elif rec[0] == nums[i]:
rec[1] += 1
else:
rec[1] -= 1
return rec[0]
https://leetcode-cn.com/problems/element-appearing-more-than-25-in-sorted-array/
给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。请你找到并返回这个整数。
示例:输入:arr = [1,2,2,6,6,6,6,7,10],输出:6
提示:1 <= arr.length <= 10^4,0 <= arr[i] <= 10^5
思路
一:原数组有序,则可以对数组进行一次遍历,并统计每个数出现的次数。只要发现某个数出现的次数超过数组 arr
长度的四分之一,那么这个数即为答案。
class Solution:
def findSpecialInteger(self, arr):
n = len(arr)
cur, cnt = arr[0], 0
for i in range(n):
if arr[i] == cur:
cnt += 1
if cnt * 4 > n:
return cur
else:
cur, cnt = arr[i], 1
return -1
二:二分法,copy了leetcode的官方题解,https://leetcode-cn.com/problems/element-appearing-more-than-25-in-sorted-array/solution/you-xu-shu-zu-zhong-chu-xian-ci-shu-chao-guo-25d-3/,
根据题目要求,满足条件的整数 x 至少在数组 arr 中出现了 span = arr.length / 4 + 1 次,那么我们可以断定:数组 arr 中的元素 arr[0], arr[span], arr[span * 2], ... 一定包含 x。
我们可以使用反证法证明上述的结论。假设 arr[0], arr[span], arr[span * 2], ... 均不为 x,由于数组 arr 已经有序,那么 x 只会连续地出现在 arr[0], arr[span], arr[span * 2], ... 中某两个相邻元素的间隔中,因此其出现的次数最多为 span - 1 次,这与它至少出现 span 次相矛盾。
有了上述的结论,我们就可以依次枚举 arr[0], arr[span], arr[span * 2], ... 中的元素,并将每个元素在数组 arr 上进行二分查找,得到其在 arr 中出现的位置区间。如果该区间的长度至少为 span,那么我们就得到了答案。
class Solution(object):
def findSpecialInteger(self, arr):
# 因为range中的步长取得n//4,该值不能为0。
if len(arr) <= 2:
return arr[0]
if len(arr) == 3:
return arr[1]
n = len(arr)
for i in range(0, n, n // 4):
cnt = self._higher(arr, arr[i]) - self._lower(arr, arr[i]) + 1
if cnt > n // 4:
return arr[i]
def _lower(self, nums, target):
# [l, r], 找到第一个等于该数的数
l, r = 0, len(nums) - 1
loc = -1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] < target:
loc = mid
l = mid + 1
else:
r = mid - 1
return loc + 1
def _higher(self, nums, target):
# [l, r], 找到最后一个等于该数的数
l, r = 0, len(nums) - 1
loc = r + 1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] <= target:
l = mid + 1
else:
loc = mid
r = mid - 1
return loc - 1
# def _lower(self, nums, target):
# l, r = 0, len(nums) - 1
# while l < r:
# mid = l + (r - l) // 2
# if nums[mid] < target:
# l = mid + 1
# else:
# r = mid
# if nums[l] == target:
# return l
# return -1
#
# def _higher(self, nums, target):
# l, r = 0, len(nums) - 1
# while l < r:
# mid = l + (r - l + 1) // 2
# if nums[mid] <= target:
# l = mid
# else:
# r = mid - 1
# if nums[r] != target:
# return -1
# return r
一个有趣的库bisect,使用时保证原数组有序。
bisect_left:在有序数组中新的元素x应该插入的位置i,并保证a[:i]
=x,i是第一个大于等于x的元素的下标,该题中为等于x的最小的下标(因为必存在等于)。 bisect_right:在有序数组中新的元素x应该插入的位置i,并保证a[:i]<=x,a[i:]>x,i是第一个大于x元素的下标,在该题中i-1是等于x的最大的下标(因为必存在等于)。
bisect_left(a, x[, lo[, hi]]) -> index
Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e < x, and all e in a[i:] have e >= x. So if x already appears in the list, i points just before the leftmost x already there.bisect_right(a, x[, lo[, hi]]) -> index
Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e <= x, and all e in a[i:] have e > x. So if x already appears in the list, i points just beyond the rightmost x already there
class Solution(object):
def findSpecialInteger(self, arr):
# 因为range中的步长取得n//4,该值不能为0。
if len(arr) <= 2:
return arr[0]
if len(arr) == 3:
return arr[1]
n = len(arr)
for i in range(0, n, n // 4):
# 与之前自定义的元素不同,这边不用加1,具体见上面文字
cnt = bisect.bisect_right(arr, arr[i]) - bisect.bisect_left(arr, arr[i])
if cnt > n // 4:
return arr[i]
https://leetcode-cn.com/problems/pascals-triangle-ii/
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:输入: 3,输出: [1,3,3,1]
进阶:你可以优化你的算法到 O(k) 空间复杂度吗?
思路
一:借用118. 杨辉三角的思路,把所有行都求出来,返回最后一行,O(n^2)的时间空间复杂度。
class Solution(object):
def getRow(self, rowIndex):
"""
:type rowIndex: int
:rtype: List[int]
"""
if rowIndex == 0:
return [1]
rec = [[1]]
for i in range(1, rowIndex + 1):
tmp = [1]
for j in range(1, len(rec[-1])):
tmp.append(rec[-1][j-1] + rec[-1][j])
tmp += [1]
rec.append(tmp)
return rec[-1]
二:我们发现,正在考虑的这一行只与上一行有关系,故只需要保留上一行,既可以只用两行,O(n^2)的时间复杂度,O(n)的空间复杂度。
class Solution(object):
def getRow(self, rowIndex):
if rowIndex == 0:
return [1]
rec = [[0] * (rowIndex+1) for _ in range(2)]
for i in range(0, rowIndex + 1):
rec[i%2][0] = 1
for j in range(1, i):
rec[i%2][j] = rec[(i+1)%2][j - 1] + rec[(i+1) % 2][j]
rec[i%2][i] = 1
return rec[rowIndex % 2]
三:组合数,copy一下leetcode大佬的题解,https://leetcode-cn.com/problems/pascals-triangle-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by--28/,
如果熟悉杨辉三角,应该记得杨辉三角其实可以看做由组合数构成。
组合数的相关公式,
class Solution(object):
def getRow(self, rowIndex):
rec = [[1] * (rowIndex + 1) for _ in range(2)]
for i in range(1, rowIndex + 1):
for j in range(1, i):
rec[i % 2][j] = rec[(i + 1) % 2][j - 1] + rec[(i + 1) % 2][j]
return rec[rowIndex % 2]
https://leetcode-cn.com/problems/contiguous-sequence-lcci/
给定一个整数数组(有正数有负数),找出总和最大的连续数列,并返回总和。
示例:输入: [-2,1,-3,4,-1,2,1,-5,4],输出: 6,解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
思路
一:用res记录截止目前的最大值,local_sum表示前面的累加和,若其小于0,则表示在其基础累加会起副作用,故丢弃所有值,重新累加。
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
res = nums[0]
local_sum = nums[0]
for i in range(1, len(nums)):
if local_sum < 0:
local_sum = nums[i]
else:
local_sum += nums[i]
res = max(res, local_sum)
return res
https://leetcode-cn.com/problems/compare-strings-by-frequency-of-the-smallest-character/
我们来定义一个函数 f(s),其中传入参数 s 是一个非空字符串;该函数的功能是统计 s 中(按字典序比较)最小字母的出现频次。例如,若 s = "dcce",那么 f(s) = 2,因为最小的字母是 "c",它出现了 2 次。现在,给你两个字符串数组待查表 queries 和词汇表 words,请你返回一个整数数组 answer 作为答案,其中每个 answer[i] 是满足 f(queries[i]) < f(W) 的词的数目,W 是词汇表 words 中的词。
示例 1:输入:queries = ["cbd"], words = ["zaaaz"],输出:[1],解释:查询 f("cbd") = 1,而 f("zaaaz") = 3 所以 f("cbd") < f("zaaaz")。
示例 2:输入:queries = ["bbb","cc"], words = ["a","aa","aaa","aaaa"],输出:[1,2],解释:第一个查询 f("bbb") < f("aaaa"),第二个查询 f("aaa") 和 f("aaaa") 都 > f("cc")。
提示:1 <= queries.length <= 2000,1 <= words.length <= 2000,1 <= queries[i].length, words[i].length <= 10,queries[i][j], words[i][j] 都是小写英文字母
思路
一:利用好条件很重要,1 <= queries[i].length, words[i].length <= 10,即所谓的f函数即helper的返回值一定是小于等于10的,故而我们可用计数排序,用rec[i]记录返回值是i的有几个元素,由于 f("aaa") 和 f("aaaa") 都 > f("cc"),在奇数排序的基础上,我们重新定义rec,rec[i]记录返回值大于等于i的有几个元素,即是原先的rec的rec[i:]的累加和。且要是严格的小于不能等于,故res[i] = rec[v + 1]。
class Solution(object):
def numSmallerByFrequency(self, queries, words):
"""
:type queries: List[str]
:type words: List[str]
:rtype: List[int]
"""
rec = [0] * 12
for i in range(len(words)):
v = self._helper(words[i])
rec[v] += 1
for i in range(9, -1, -1):
rec[i] += rec[i+1]
res = [0] * len(queries)
for i in range(len(queries)):
v = self._helper(queries[i])
res[i] = rec[v + 1]
return res
def _helper(self, s):
rec = [0] * 26
for c in s:
rec[ord(c) - ord("a")] += 1
for i in range(26):
if rec[i] != 0:
return rec[i]
return -1
https://leetcode-cn.com/problems/duplicate-zeros/
给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。注意:请不要在超过该数组长度的位置写入元素。要求:请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。
示例 1:输入:[1,0,2,3,0,4,5,0],输出:null,解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]
示例 2:输入:[1,2,3],输出:null,解释:调用函数后,输入的数组将被修改为:[1,2,3]
提示:1 <= arr.length <= 10000,0 <= arr[i] <= 9
思路
一:借助额外空间
class Solution(object):
def duplicateZeros(self, arr):
"""
:type arr: List[int]
:rtype: None Do not return anything, modify arr in-place instead.
"""
rec, j, cnt = arr[:], 0, 0
for i in range(len(arr)):
if cnt > 0:
arr[i] = 0
cnt -= 1
elif rec[j] == 0:
arr[i] = 0
j += 1
cnt += 1
else:
arr[i] = rec[j]
j += 1
return
二:不借助额外的空间,原址修改,做两次遍历,第一次遍历确定新的列表中的最后一个元素是原列表哪个下标(last_i)对应的元素(若最后一个元素为0,还要标记这个0是否要复写)。第二次从右往左遍历填入数据。
class Solution(object):
def duplicateZeros(self, arr):
last_i, i, two_zero = 0, 0, True
# 第一次遍历确定新的列表中的最后一个元素是原列表哪个下标(last_i)对应的元素
while i < len(arr):
if arr[last_i] != 0:
i += 1
else:
i += 2
# 若最后一个元素为0,还要标记这个0是否要复写
if i > len(arr):
two_zero = False
last_i += 1
last_i, i = last_i - 1, len(arr) - 1
while last_i >= 0:
if arr[last_i] != 0:
arr[i] = arr[last_i]
else:
if not two_zero and i == len(arr) -1:
arr[i] = 0
else:
arr[i] = 0
i -= 1
arr[i] = 0
i -= 1
last_i -= 1
https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array/
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
示例:输入:[4,3,2,7,8,2,3,1],输出:[5,6]
思路
一:借助额外空间,由于值域是有范围的,可开辟一个固定空间rec,遍历数组将nums中值作为下标置1,遍历rec,哪个下标为0,就是缺失值。
class Solution(object):
def findDisappearedNumbers(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
rec = [0] * (len(nums) + 1)
for num in nums:
rec[num] += 1
res = []
for i in range(1, len(nums) + 1):
if rec[i] == 0:
res.append(i)
return res
二:同样因为题目值域的特殊性,且找的是缺失的数字。把 abs(nums[i])-1 索引位置的元素标记为负数。即若该位置元素大于零,则将该位置乘上-1,nums[abs(nums[i])−1]×−1 。最后遍历列表,若i下标的元素大于0(没修改过),则i+1是缺失的。其实出现1次也好做,只需要将nums[abs(nums[i])−1]=nums[abs(nums[i])−1]×−1,改成nums[abs(nums[i])−1]=abs(nums[abs(nums[i])−1])×−1,找小于0的即可
class Solution(object):
def findDisappearedNumbers(self, nums):
for i in range(len(nums)):
new_idx = abs(nums[i]) - 1
if nums[new_idx] > 0:
nums[new_idx] = -1 * nums[new_idx]
res = []
for i in range(len(nums)):
if nums[i] > 0:
res.append(i + 1)
return res
https://leetcode-cn.com/problems/find-all-duplicates-in-an-array/
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。找到所有出现两次的元素。你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:输入:[4,3,2,7,8,2,3,1],输出:[2,3]
思路
一:仿照上一题的解法。
class Solution(object):
def findDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
res = []
for i in range(len(nums)):
new_idx = abs(nums[i]) - 1
if nums[new_idx] < 0:
# 小于0,表示之前遍历过,现在又遍历到,是解
res.append(new_idx + 1)
else:
# 大于0,表示之前没有遍历到,此时遍历到了标负
nums[new_idx] = -1 * abs(nums[new_idx])
return res