数组双指针(Leetcode题型归纳)
1.前言
在系统复习一遍数组的理论基础后,我们开始归纳Leetcode中的一种题型:数组双指针。归纳是很痛苦的过程,也是最有成就感的事情,希望大家也能多指出我的不足。
2.双指针基础
所谓双指针算法,就是指的是在遍历的过程中,不是普通的使用单个指针进行循环访问,而是使用两个相同方向或者相反方向的指针进行扫描,从而达到相应的目的。双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算,降低时间复杂度。

一般来说,双指针算法分成下面四类:
- 相向双指针
- 同向双指针 - 快慢指针
- 同向双指针 - 滑动窗口
- 分离双指针
3.相向双指针
3.1定义
指在有序数组中,将指向最左侧的索引定义为左指针 (left),最右侧的定义为右指针 (right),然后从两头向中间进行数组遍历。
相关题目如下:

3.2真题分析
3.2.1两数之和(1)
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
You can return the answer in any order.
Example 1:
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Output: Because nums[0] + nums[1] == 9, we return [0, 1].
Example 2:
Input: nums = [3,2,4], target = 6
Output: [1,2]
Example 3:
Input: nums = [3,3], target = 6
Output: [0,1]
Constraints:
2 <= nums.length <= 104-109 <= nums[i] <= 109-109 <= target <= 109- Only one valid answer exists.
Follow-up: Can you come up with an algorithm that is less than O(n2) time complexity?
后面也会尽量附上中文版本题目方便大家读题:

一开始,我会想到去用暴力解法吧:枚举数组中的每一个数 x,寻找数组中是否存在 target - x。当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。
#两数之和-暴力解法
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
l = len(nums)
for i in range(l-1):
for j in range(i+1,l):
if nums[i] + nums[j] == target:
return [i,j]
然而,暴力解法时间复杂度是O(n*2),不太理想,原因是寻找target - x这部分时间复杂度比较高。所以,我又探索了用哈希表去解决这个问题。简单来说,我们可以把哈希表看作一个字典,这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。






#两数之和-哈希解法
class Solution:
def twoSum(self, nums, target):
map = {
} #创建一个哈希表
for i in range(len(nums)):
if target - nums[i] not in map:
map[nums[i]] = i
else:
return map[target - nums[i]], i
这时时间复杂度是O(n)了。But,这好像跟双指针没什么关系欸,我们现在来讲双指针的思路:L指针用来指向第一个值,R指针用来从第L指针的后面查找数组中是否含有和L指针指向值和为目标值的数。两个指针分别从左从右开始扫描,每次判断这两个数相加是不是target,如果小了,那就把左边的指针向右移,同理右指针。




#两数之和-双指针解法
class Solution:
def twoSum(self, nums, target):
array = nums.copy() #浅拷贝,保留原来数组
nums.sort() #排序
i, j = 0, len(nums)-1 #两指针一头一尾
while i<j:
s = nums[i] + nums[j]
if s<target:
i += 1
elif s>target:
j -= 1
else:
break
res = []
for k in range(len(nums)):
if array[k] == nums[i] or array[k] == nums[j]:
res.append(k)
return res
这时候时间复杂度也是O(n)的。然而,如果我们把题目改一下:

此时我们不再需要返回index,而是返回值了。那么有个好处是,我不用再copy数组了,因为之前是为了防止index改变了。
#两数之和-魔改
class Solution:
def twoSum(self, nums, target):
nums.sort() #先对数组排序
lo, hi = 0, len(nums)-1 #左右指针
while lo<hi:
s = nums[lo] + nums[hi]
if s<target:
lo += 1
elif s>target:
hi -= 1
elif s == target:
return [nums[lo],nums[hi]]
emmm我再改一下题目:

这个时候要返回所有和为target的元素对了,而且不能重复哦,那我就把这些元素对存到一个数组res里咯。另外如果有下图这样的情况,可能就会出现重复元素对的情况,就要去跳过了。

#两数之和-再魔改
class Solution:
def twoSumTarget(self, nums, target):
nums.sort()#先对数组排序,这个内置的算法是O(nlogn)
res = []
lo, hi = 0, len(nums)-1 #左右指针
left = nums[lo]
right = nums[hi]
while lo<hi:
s = nums[lo] + nums[hi]
if s<target:
lo += 1
elif s>target:
hi -= 1
elif s == target:
res.append([nums[lo],nums[hi]])
while lo < hi and nums[lo] == left:
lo += 1
while lo < hi and nums[hi] == right:
hi -= 1
return res
这个例子的时间复杂度是O(nlogn)的(因为sort方法是这样),是比O(n)大的。
3.2.2两数之和-输入有序数组(167)
Given an array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number.
Return the indices of the two numbers (1-indexed) as an integer array answer of size 2, where 1 <= answer[0] < answer[1] <= numbers.length.
The tests are generated such that there is exactly one solution. You may not use the same element twice.
Example 1:
Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.
Example 2:
Input: numbers = [2,3,4], target = 6
Output: [1,3]
Example 3:
Input: numbers = [-1,0], target = -1
Output: [1,2]

有了上一题的基础,这题不会太难。思路如下:
1. 使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
2. 如果两个指针指向元素的和 sum == target,那么得到要求的结果;
3. 如果 sum > target,移动较大的元素,使 sum 变小一些;如果 sum < target,移动较小的元素,使 sum变大一些。
#167两数之和
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
low, high = 0, len(numbers) - 1
while low < high:
total = numbers[low] + numbers[high]
if total == targe

&spm=1001.2101.3001.5002&articleId=120058632&d=1&t=3&u=27d1a7ff56ae4e33947df7f219a7f827)

被折叠的 条评论
为什么被折叠?



