数组综合标准答案¶
记忆核心:数组题先判断是哪类动作:排序合并、原地交换、前后缀、投票、异或、快慢指针。
31 下一个排列¶
- 题目:把数组改成字典序中的下一个更大排列;如果已经最大,就变成最小排列。
- 思路:从右找第一个下降点,再找右侧刚好比它大的数交换,最后反转后缀。
- 标准答案:
class Solution:
def nextPermutation(self, nums):
n = len(nums)
i = n - 2
while i >= 0 and nums[i] >= nums[i + 1]:
i -= 1
if i >= 0:
j = n - 1
while nums[j] <= nums[i]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
l, r = i + 1, n - 1
while l < r:
nums[l], nums[r] = nums[r], nums[l]
l += 1
r -= 1
- 小坑:完全降序时
i会变成 -1,然后反转整个数组。
41 缺失的第一个正数¶
- 题目:在无序数组中找没有出现的最小正整数,要求 O(n) 时间、O(1) 额外空间。
- 思路:把数字
x尽量放到下标x-1的位置;最后第一个不对位的位置就是答案。 - 标准答案:
class Solution:
def firstMissingPositive(self, nums):
n = len(nums)
for i in range(n):
while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
j = nums[i] - 1
nums[i], nums[j] = nums[j], nums[i]
for i in range(n):
if nums[i] != i + 1:
return i + 1
return n + 1
- 小坑:while 条件里要防止重复数字导致死循环。
53 最大子数组和¶
- 题目:找连续子数组,使元素和最大。
- 思路:如果前面的和是负数,不如从当前数重新开始。
- 标准答案:
class Solution:
def maxSubArray(self, nums):
cur = ans = nums[0]
for x in nums[1:]:
cur = max(x, cur + x)
ans = max(ans, cur)
return ans
- 小坑:答案初始化为
nums[0],才能处理全负数。
56 合并区间¶
- 题目:给若干区间,合并所有重叠区间。
- 思路:按左端点排序;当前区间和答案最后一个区间重叠就扩右端点,否则新开一段。
- 标准答案:
class Solution:
def merge(self, intervals):
intervals.sort(key=lambda x: x[0])
ans = []
for l, r in intervals:
if not ans or ans[-1][1] < l:
ans.append([l, r])
else:
ans[-1][1] = max(ans[-1][1], r)
return ans
- 小坑:判断不重叠是
ans[-1][1] < l,等于时也要合并。
75 颜色分类¶
- 题目:数组只包含 0、1、2,原地排序成 0 在前、1 中间、2 在后。
- 思路:三指针。
p0放下一个 0,p2放下一个 2,i扫描当前元素。 - 标准答案:
class Solution:
def sortColors(self, nums):
p0, i, p2 = 0, 0, len(nums) - 1
while i <= p2:
if nums[i] == 0:
nums[p0], nums[i] = nums[i], nums[p0]
p0 += 1
i += 1
elif nums[i] == 2:
nums[p2], nums[i] = nums[i], nums[p2]
p2 -= 1
else:
i += 1
- 小坑:换来右边的数还没检查,所以遇到 2 时
i不动。
136 只出现一次的数字¶
- 题目:数组里除了一个数只出现一次,其余都出现两次,找这个数。
- 思路:异或。相同数字异或为 0,0 异或任何数还是它自己。
- 标准答案:
- 小坑:异或符号是
^。
169 多数元素¶
- 题目:找数组中出现次数超过一半的元素。
- 思路:摩尔投票。不同元素互相抵消,最后剩下的一定是多数元素。
- 标准答案:
class Solution:
def majorityElement(self, nums):
cand = None
count = 0
for x in nums:
if count == 0:
cand = x
count += 1 if x == cand else -1
return cand
- 小坑:题目保证多数元素存在,所以最后不用再验证。
189 轮转数组¶
- 题目:把数组向右轮转
k步,要求原地修改。 - 思路:三次反转:整体反转,前
k个反转,后面反转。 - 标准答案:
class Solution:
def rotate(self, nums, k):
n = len(nums)
k %= n
def rev(l, r):
while l < r:
nums[l], nums[r] = nums[r], nums[l]
l += 1
r -= 1
rev(0, n - 1)
rev(0, k - 1)
rev(k, n - 1)
- 小坑:先
k %= n,否则 k 可能大于数组长度。
238 除自身以外数组的乘积¶
- 题目:返回数组
answer,其中answer[i]等于除了nums[i]以外所有数的乘积,不能用除法。 - 思路:先把左侧乘积写进答案,再从右往左乘上右侧乘积。
- 标准答案:
class Solution:
def productExceptSelf(self, nums):
n = len(nums)
ans = [1] * n
left = 1
for i in range(n):
ans[i] = left
left *= nums[i]
right = 1
for i in range(n - 1, -1, -1):
ans[i] *= right
right *= nums[i]
return ans
- 小坑:
ans[i]先存左边乘积,再乘右边乘积。
287 寻找重复数¶
- 题目:数组长度为
n+1,数字范围是1..n,只有一个重复数,找出来。 - 思路:把数组看成链表,
i -> nums[i]。重复数就是环入口,用快慢指针找。 - 标准答案:
class Solution:
def findDuplicate(self, nums):
slow = nums[0]
fast = nums[0]
while True:
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast:
break
p = nums[0]
while p != slow:
p = nums[p]
slow = nums[slow]
return p
- 小坑:这里的“next 指针”是
nums[i],不是i + 1。