leetcode(1)数组系列问题
27. 移除元素
用快慢指针,快指针将慢指针位置的数覆盖
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
fast, slow = 0, 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
26. 删除有序数组中的重复项
注意:与27. 移除元素 的区别是fast要在slow前面一个位置,并且上一题是先覆盖再移动slow下标,本题是先移动slow下标再覆盖,因此返回数组长度要+1
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
fast, slow = 1, 0
while fast < len(nums):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
fast += 1
return slow + 1
283. 移动零
注意:与27. 移除元素 的区别是本题是交换fast和slow的数而不是覆盖
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
fast, slow = 0, 0
while fast < len(nums):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
fast += 1
return
977. 有序数组的平方
双指针比较首尾平方的大小,必须先指定数组大小
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
n = len(nums)
res = [0] * n
left, right = 0, n - 1
i = n - 1
while i >= 0:
if nums[left] * nums[left] > nums[right] * nums[right]:
res[i] = nums [left] * nums[left]
left += 1
else:
res[i] = nums [right] * nums[right]
right -= 1
i -= 1
return res
59. 螺旋矩阵 II
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
res = [[0]*n for _ in range(n)]
num = 0
left, right = 0, n
up, down = 0, n
while num < n * n:
for i in range(left, right):
num += 1
res[up][i] = num
up += 1
# if up > down - 1: break 可以省略
for i in range(up, down):
num += 1
res[i][right - 1] = num
right -= 1
# if right - 1 < left: break
for i in range(right - 1, left - 1, -1): # 是right - 1
num += 1
res[down - 1][i] = num
down -= 1
# if down - 1 < up: break
for i in range(down - 1, up - 1, -1):
num += 1
res[i][left] = num
left += 1
# if left > right - 1: break
return res
54. 螺旋矩阵
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
m = len(matrix)
if m == 0: return []
n = len(matrix[0])
left, right = 0, n - 1
up, down = 0, m - 1
res = []
while len(res) < m * n:
for i in range(left, right + 1):
res.append(matrix[up][i])
up += 1
if up > down: break # 不可省略
for i in range(up, down + 1):
res.append(matrix[i][right])
right -= 1
if right < left: break
for i in range(right, left - 1, -1):
res.append(matrix[down][i])
down -= 1
if down < up: break
for i in range(down, up - 1, -1):
res.append(matrix[i][left])
left += 1
if left > right: break
return res
268. 丢失的数字
将从 0到 n 的全部整数之和记为 total,将数组 nums 的元素之和记为 arrSum,则 arrSum 比 total 少了丢失的一个数字,因此丢失的数字即为 total 与 arrSum 之差。
class Solution:
def missingNumber(self, nums: List[int]) -> int:
n = len(nums)
total = n * (n + 1) // 2 #注意项数是n + 1,首项加尾项乘以项数除以二
arrSum = sum(i for i in nums)
miss = total - arrSum
return miss
41. 缺失的第一个正数
遍历一次数组把大于等于1的和小于数组大小的值放到原数组对应位置,然后再遍历一次数组查当前下标是否和值对应,如果不对应那这个下标就是答案,否则遍历完都没出现那么答案就是数组长度加1。
在完成交换后,新的nums[i] 可能又在 [1, N]内,需要继续进行交换操作,直到 nums[i] 不在 [1, N]内
注意到上面的方法可能会陷入死循环。如果 nums[i] 恰好与nums[nums[i] −1] 相等,那么就会无限交换下去。此时我们有 nums[i]=nums[nums[i] −1],说明 nums[i] 已经出现在了正确的位置。因此我们可以跳出循环,开始遍历下一个数。
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
#新的nums[i] 可能又在 [1, N]内,需要继续进行交换操作,因此使用while
while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
# nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]错误的顺序
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
print(nums)
for i in range(n):
if nums[i] != i + 1:
return i + 1
return n + 1
915. 分割数组
一次遍历,当后面又出现小值时,更新左数组的右边界、最大值
class Solution:
def partitionDisjoint(self, nums: List[int]) -> int:
left_max = cur_max = nums[0]
res = 0
for i in range(1, len(nums) - 1):
cur_max = max(cur_max, nums[i])
if nums[i] < left_max:
res = i
left_max = cur_max
return res + 1
88. 合并两个有序数组
同一题:面试题 10.01. 合并排序的数组
用while循环,先判断A、B是否遍历完,再判断大小关系
class Solution:
def merge(self, A: List[int], m: int, B: List[int], n: int) -> None:
tail = m + n - 1
p, q = m - 1, n - 1
while p >= 0 or q >= 0:
if p == -1:
A[tail] = B[q]
q -= 1
elif q == -1:
A[tail] = A[p]
p -= 1
elif A[p] >= B[q]:
A[tail] = A[p]
p -= 1
else:
A[tail] = B[q]
q -= 1
tail -= 1
315. 计算右侧小于当前元素的个数
从右往左遍历,加到排序数组中,直接查看当前元素的下标,就是右侧小于当前元素的个数
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
from sortedcontainers import SortedList
s = SortedList()
res = [0] * len(nums)
for i in range(len(nums) - 1, -1, -1):
s.add(nums[i])
res[i] = s.index(nums[i])
return res
31. 下一个排列
从后往前找到一个temp[i] < temp[i + 1],把temp[i]作为一个较小数,然后从后往前找到第一个比temp[i]大的数temp[j],交换temp[i]和temp[j],这样[i + 1, n)就是单调递减的,逆转一下变成单调递增的
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
m = len(nums)
i = m - 2
while i >= 0 and nums[i] >= nums[i + 1]:
i -= 1
if i == -1:return nums.reverse()
j = m - 1
while nums[j] <= nums[i]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
nums[i + 1:] = sorted(nums[i + 1:])
556. 下一个更大元素 III
跟31本质上是同一题,先把数值转成列表
class Solution:
def nextGreaterElement(self, n: int) -> int:
nums = list(str(n))
m = len(nums)
i = m - 2
while i >= 0 and nums[i] >= nums[i + 1]: # 与后一个比较
i -= 1
if i == -1:return -1
j = m - 1
while nums[j] <= nums[i]: # 与i比较,注意有=
j -= 1
nums[i], nums[j] = nums[j], nums[i]
nums[i + 1:] = nums[i + 1:][::-1]
res = int(''.join(nums))
return res if res < 2 ** 31 else -1
217. 存在重复元素
取巧做法:
时间复杂度 : O(n)
空间复杂度 : O(n)
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
return not len(nums) == len(set(nums))
用字典:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
hashset = {}
for n in nums:
if n not in hashset:
hashset[n] = 1
else:
return True
return False
用集合:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
hashset = set()
for n in nums:
if n not in hashset:
hashset.add(n)
else:
return True
return False
排序后判断前后数字是否一样
时间复杂度 : O(nlogn)。即排序的时间复杂度。扫描的时间复杂度 O(n) 可忽略。
空间复杂度 : O(1)。 考虑递归调用栈的深度就是O(logn)
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
nums.sort()
for i in range(1, len(nums)):
if nums[i - 1] == nums[i]:
return True
return False
775. 全局倒置与局部倒置
数列排序后就就是0,1,2.......N,刚好和下标是对应的.如果一个数和他的下标偏移量超过了1,即是和他的有序排列偏移超过了1,那么全局偏移和局部偏移必然不相等
class Solution:
def isIdealPermutation(self, nums: List[int]) -> bool:
for i, n in enumerate(nums):
if abs(n - i) > 1:
return False
return True
1752. 检查数组是否经排序和轮转得到
只有一个或0个数字比后一个大,则可以通过旋转得到
class Solution:
def check(self, nums: List[int]) -> bool:
n = len(nums)
return sum(nums[i] > nums[(i + 1) % n] for i in range(n)) <= 1
1827. 最少操作使数组递增
一次遍历,记录最大值
class Solution:
def minOperations(self, nums: List[int]) -> int:
mx = nums[0]
res = 0
for i in range(1, len(nums)):
mx = max(mx + 1, nums[i])
res += max(0, mx - nums[i])
return res
2032. 至少在两个数组中出现的值
用intersection函数求集合交集
class Solution:
def twoOutOfThree(self, nums1: List[int], nums2: List[int], nums3: List[int]) -> List[int]:
return list(set(list(set(nums1).intersection(set(nums2))) + list(set(nums2).intersection(set(nums3))) + list(set(nums1).intersection(set(nums3)))))