283. 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
Solution
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
fast += 1
15. 三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
注意,输出的顺序和三元组的顺序并不重要。
Solution
每次将较矮的那根柱子的指针向中心移动,因为移动较高的柱子不会增加水容量。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res= []
nums.sort()
for i in range(len(nums) - 2):
if nums[i] > 0:
break
left, right = i + 1, len(nums) - 1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s > 0:
right -= 1
elif s < 0:
left += 1
else:
if [nums[i], nums[left], nums[right]] not in res:
res.append([nums[i], nums[left], nums[right]])
left += 1
return res
如果在每次找到三元组时,都使用if [nums[i], nums[left], nums[right]] not in res:来避免重复,效率是较低的,我们可以:
-
当找到一个解时,要确保左右指针指向的数字在下一次迭代中不同,以避免重复的三元组。这意味着在找到解后,我们不仅需要移动左或右指针,还需要确保它们不再指向相同的数字。
-
为固定点i添加一个检查以跳过重复值。这是一个非常重要的步骤,因为如果不这样做,结果中可能会出现重复的三元组,即使您的双指针处理方式是正确的。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
for i in range(len(nums) - 2):
if nums[i] > 0:
break
# 跳过重复的数
if i > 0 and nums[i] == nums[i - 1]:
continue
left, right = i + 1, len(nums) - 1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s > 0:
right -= 1
elif s < 0:
left += 1
else:
res.append([nums[i], nums[left], nums[right]])
# 跳过重复的数
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
return res
11. 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
Solution
class Solution:
def maxArea(self, height: List[int]) -> int:
left, right = 0, len(height) - 1
max_area = 0
while left <= right:
if height[left] < height[right]:
max_area = max(max_area, (right - left) * height[left])
left += 1
else:
max_area = max(max_area, (right - left) * height[right])
right -= 1
return max_area
42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
Solution
朴素的做法是对于数组 height 中的每个位置,分别向左和向右扫描并记录左边和右边的最大高度,然后取两者之间的最小值,与当前高度的差值就是该位置能接的雨水量。
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
n = len(height)
rain = 0
for i in range(n):
max_left, max_right = 0, 0
left, right = i-1, i+1
while left >= 0:
max_left = max(height[left], max_left)
left -= 1
while right < n:
max_right = max(height[right], max_right)
right += 1
rain += max(0, min(max_left, max_right) - height[i])
return rain
需要注意的是,我们需要在减去当前高度之前,确保 min(max_left, max_right) 大于当前高度。
现在我们用两个长度为 n 的数组 max_left 和 max_right,对于 0≤i<n,max_left 表示下标 i 及其左边的位置中 height 的最大高度,max_right 表示下标 i 及其右边的位置中 height 的最大高度,通过动态规划的方法正向遍历数组 height 得到数组 max_left 的每个元素值,反向遍历数组 height 得到数组 max_right 的每个元素值。
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
n = len(height)
max_left, max_right = [0] * n, [0] * n
max_left[0] = height[0]
max_right[n - 1] = height[n - 1]
for i in range(1, n):
max_left[i] = max(max_left[i - 1], height[i])
max_right = [0] * (n - 1) + [height[n - 1]]
for i in range(n - 2, -1, -1):
max_right[i] = max(max_right[i + 1], height[i])
rain = sum(min(max_left[i], max_right[i]) - height[i] for i in range(n))
return rain
在这个解决方法中,我使用了两个不同的循环来得到 max_left 和 max_right 的值,这是因为两个数组开始的位置和遍历的方向都不相同。
如果我们可以做到,在每次算出左边的 max_left 和 右边的 max_right 后,都能算出某一位置的 rain,我们就可以用双指针的方法实现一个时间复杂度为O(n),空间复杂度为O(1)的算法。
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
n = len(height)
left, right = 0, n-1
max_left, max_right = 0, 0
rain = 0
while left < right:
max_left = max(max_left, height[left])
max_right = max(max_right, height[right])
if height[left] <= height[right]:
rain += max_left - height[left]
left += 1
else:
rain += max_right - height[right]
right -= 1
return rain
如果 height[left] < height[right],那么对于 left 指向的柱子来说,它的右侧一定有柱子的高度大于等于 height[right](也就是说,right_max 一定大于等于 height[left])。因此,我们就可以计算出left指向的柱子所能接的雨水量,然后 left 向右移动一位。如果height[left] >= height[right],同理我们可以计算出 right 指向的柱子所能接的雨水量,然后 right 向左移动一位。