定义
双指针的题,即利用两个指针去遍历数组。往往涉及到有序的范围,可以通过左右的加减来减少暴力遍历的次数。
一. 盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,
垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可
以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
思路:
设置双指针 i,j 分别位于容器壁两端,根据规则移动指针(后续说明),并且更新面积最大值 res,直到 i == j 时返回 res
解答:
class Solution:
def maxArea(self, height: List[int]) -> int:
length = len(height)
i, j = 0, length - 1
res_max = min(height[i],height[j]) * (j - i)
for k in range(length-2):
if height[i] <= height[j]:
i += 1
else:
j -= 1
temp_max = min(height[i] , height[j]) * (j - i)
res_max = max(temp_max,res_max)
return res_max
二.三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1], [-1, -1, 2]
]
思路:
1. 特判,对于数组长度 n,如果数组为 null 或者数组长度小于 3,返回 []。
2. 对数组进行排序。
3. 遍历排序后数组:
若 nums[i]>0nums[i]>0:因为已经排序好,所以后面不可能有三个数加和等于 0,直接返回结果。
对于重复元素:跳过,避免出现重复解
令左指针 L=i+1 ,右指针 R=n-1,当 L<R 时,执行循环:
当 nums[i]+nums[L]+nums[R]==0 nums[i]+nums[L]+nums[R]==0,执行循环,判断左界和右界是否和下一位置重复,去除重复解。并同时将 L,RL,R 移到下一位置,寻找新的解
若和大于 0,说明 nums[R]nums[R] 太大,RR 左移
若和小于 0,说明 nums[L]nums[L] 太小,LL 右移
解答:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
'''
# 1、固定k值
# 跳过k值为正的数
# i+1的值和k相等跳过
# 2、设置双指针
# 2.1 如果三者之和大于0
# R -1
# 2.2 如果三者之和小于0
# L + 1
# 2.3 如果三者之和等于0
# 跳过R和L值相等的。
# 同时R和L的值分别-1、+1
'''
nums.sort()
length, res = len(nums), []
if length < 3:
return res
for i, k in enumerate(nums):
if i > 0 and k == nums[i - 1]:
continue
L, R = i + 1, length - 1
while L < R:
L_n, R_n = nums[L], nums[R]
temp_sum = sum([k, L_n, R_n])
if temp_sum > 0:
R -= 1
elif temp_sum < 0:
L += 1
else:
res.append([k, L_n, R_n])
while L < R and L_n == nums[L + 1]:
L += 1
while L < R and R_n == nums[R - 1]:
R -= 1
L += 1
R -= 1
return res
四数之和
在三数和的基础上新增一个for。
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
length, res = len(nums), []
if length < 4:
return []
for x in range(length - 3):
m = nums[x]
if (x > 0 and m == nums[x - 1]) or (m + sum(nums[length - 3:]) < target):
continue
if sum(nums[x:x + 4]) > target:
break
for i in range(x + 1, length - 2):
k = nums[i]
if (i > x + 1 and k == nums[i - 1]) or (m + k + sum(nums[length - 2:]) < target):
continue
if m + sum(nums[i:i + 3]) > target:
break
L, R = i + 1, length - 1
while L < R:
L_n, R_n = nums[L], nums[R]
temp_sum = sum([k, L_n, R_n, m])
if temp_sum > target:
R -= 1
elif temp_sum < target:
L += 1
else:
res.append([m, k, L_n, R_n])
while L < R and L_n == nums[L + 1]:
L += 1
L += 1
while L < R and R_n == nums[R - 1]:
R -= 1
R -= 1
return res
三.删除排序数组中的重复项
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
解答:
# 普通解答
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
for index,n in enumerate(nums,0):
temp_list = nums[index+1:]
for i in range(temp_list.count(n)):
temp_list.remove(n)
nums[:]=nums[:index+1]+temp_list
return len(nums)
# 双指针
class Solution:
def removeDuplicates(self, nums) -> int:
if len(nums) <= 1:
return len(nums)
i, j = 0, 1
while j < len(nums):
if nums[i] == nums[j]:
nums.pop(j)
else:
j += 1
i = j - 1
return j
字符串翻转或轮转数组
给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
解答
# 方法一
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
length = len(nums)
if k > length:
k = k % length
for i in range(k):
nums.insert(0, nums.pop())
# 方法二
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
length = len(nums)
if k > length:
k = k % length
def reverse(i, j):
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
# [7, 6, 5, 4, 3, 2, 1]
# [5, 6, 7, 4, 3, 2, 1]
# [5, 6, 7, 1, 2, 3, 4]
reverse(0, length - 1)
reverse(0, k - 1)
reverse(k, length - 1)