双指针

双指针

类型

  • 首尾双指针
  • 同向双指针
  • 分离双指针

首尾双指针

两数之和

LeetCode/167. 两数之和 II - 输入有序数组

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
-返回的下标值(index1 和 index2)不是从零开始的。
-你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

思路
  • 定义左指针指向最小元素(首),右指针指向最大元素(尾)
  • 移动 如果左右指针之和大于目标数,右指针 -1 。左右指针之和小于目标数,左指针 +1。相等,则输出左右指针位置
  • 循环在左右指针相等时结束
class Solution:
    def twoSum(self, nums: List[int], val: int) -> List[int]:
        left = 0
        right = len(nums) - 1
        while left < right:
            curr = nums[left] + nums[right]
            if curr == val:
                return [left + 1, right + 1]
                left += 1
                right -= 1
            else:
                if curr > val:
                    right -= 1
                else:
                    left += 1

三数之和

LeetCode/15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

思路
  • 排序
  • 在双指针基础上,遍历列表,第一个值依次为 nums[i]。 左指针指向第一个值后一个数,右指针指向最后一个数
  • 移动,当三数和小于指定值时,左指针 +1。当三数和大于指定值时,右指针 -1。当存在相同指时,左右指针各移一次。
  • 当找到一个值之后,按序放入空列表[nums[i],nums[left],nums[right]]
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:    
        nums.sort()
        res = []
        for i in range(len(nums)-2):
            if i > 0 and nums[i] == nums[i-1]:
                continue
            left = i + 1
            right = len(nums) - 1
            while left < right:
                if nums[left] + nums[right] + nums[i] > 0:
                    right -= 1
                elif nums[left] + nums[right] + nums[i] < 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

二分查找

LeetCode/704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。

思路
  • 左右指针
  • 对比左右指针和中间值(左右指针的中间值)大小,如果中间值大于目标值,右指针赋值中间值,产生新的中间值。如果中间值小于目标值,左指针赋值中间值,产生新的中间值。
class Solution:
    def search(self, nums: List[int], target: int) -> int:
		left = 0
	    right = len(nums) - 1
	    while left <= right:
	        if target not in nums:
	            return -1
	        if target == nums[left]:
	            return left
	        elif target == nums[right]:
	            return right
	        else:
	            mid = (left + right)//2
	            if target < nums[mid]:
	                right = mid
	            elif target > nums[mid]:
	                left = mid
	            else:
	                return mid
去除多余判断
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = -1
        right = len(nums)
        while left <= right:
        # 改进mid计算方法
            mid = left + (right - left) // 2
            if target < nums[mid]:
                right = mid
            elif target > nums[mid]:
                left = mid
            else:
                return mid
        return -1

提高:最接近的三数之和

LeetCode/16. 最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4

思路
  • 相比于三数之和增加了一个res,将最小的距离差保留下来,便可求出最优解
class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()
        min = abs(nums[0] + nums[1] + nums[2]-target)
        res = nums[0] + nums[1] + nums[2]
        for i in range(len(nums)-2):
            lefe = i + 1
            right = len(nums)-1
            while lefe < right:
                sum1 = nums[i] + nums[lefe]+nums[right]
                if abs(sum1 - target) < min:
                    min = abs(sum1 - target)
                    res = sum1
                if sum1 > target:
                    right -= 1
                elif sum1 < target:
                    lefe += 1
                else:
                    return sum1
        return res

同向双指针

删除重复项

LeetCode/26. 删除排序数组中的重复项

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 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。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

思路
  • 快慢指针,快指针遍历列表,慢指针修改列表。
  • 开始时快指针比慢指针快一步,快慢指针同时开始移动。如果慢指针等于快指针,快指针移动。当不同时,令慢指针等于快指针,然后慢指针移动
class Solution(object):
    def removeDuplicates(self, nums: List[int]) -> int:
        slow = 0
        fast = 1
        while fast < len(nums):
            if nums[slow] == nums[fast]:
                fast += 1
            else:
                slow += 1
                nums[slow] = nums[fast]
                fast += 1
        return slow+1

移除元素

LeetCode/27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

思路
  • 快慢指针同时开始移动,快指针遍历列表,慢指针修改列表
  • 同时移动时,快指针赋值给慢指针,列表不变
  • 当快指针遇到目标值时,只移动快指针,慢指针不移动,慢指针覆盖之后的值
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow = 0
        fast = 0
        while fast < len(nums):
            if nums[fast] != val:
                nums[slow] = nums[fast]
                slow += 1
                fast += 1
            else:
                fast += 1
        return slow

移动零

LeetCode/283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。

思路
  • 快慢指针
  • 将未被赋值的值赋值为零
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        slow = 0
        fast = 0
        while fast < len(nums)-1:
            if nums[fast] == 0:
                nums[slow] = nums[fast+1]
                fast += 1
            else:
                slow += 1
                fast += 1
                nums[slow] = nums[fast]
        for i in range(slow+1,len(nums)):
            nums[i] = 0

提高:删除重复项

LeetCode/80. 删除排序数组中的重复项 II

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3],
函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,1,2,3,3],
函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

思路
  • 在之前删除重复项基础上增加一步判断flag
  • 通过判断赋给慢指针(i)两次快指针(j)的值
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        if not nums:
            return 0
        i = 0
        flag = False
        for j in range(1, len(nums)):
            if nums[j] != nums[i]:
                i += 1
                nums[i] = nums[j]
                flag = False
            else:
                if not flag:
                    i += 1
                    nums[i] = nums[j]
                    flag = True
        return i+1

分离双指针

取交集

LeetCode/349. 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
说明:
输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。

思路
  • 排序,分离指针
  • 将两个列表排序
  • 如果指针一小于指针二,指针一移动。如果指针二小于指针一,指针二移动。当两数相等则取值
  • 取出的值去重
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()
        i,j = 0,0
        num_set = set()
        while i < len(nums1) and j < len(nums2):
            if nums1[i] > nums2[j]:
                j += 1
            elif nums1[i] < nums2[j]:
                i += 1
            else:
                num_set.add(nums1[i])
                i += 1
                j += 1
        return num_set
LeetCode/

合并两数组

88. 合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]

思路
  • 分离指针,从后往前遍历
  • 让数组一从右到左分别赋值两数组中的数,让被赋的值是两数组中最大的。
  • 确保头的值
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        i = m -1
        j = n - 1
        k = m + n-1
        while i >= 0 and j >= 0:
            if nums1[i] >= nums2[j]:
                nums1[k] = nums1[i]
                i -= 1
            else:
                nums1[k] = nums2[j]
                j -= 1
            k -= 1
        while i >= 0:
            nums1[k] = nums1[i]
            k -= 1
            i -= 1
        while j >=0:
            nums1[k] = nums2[j]
            k -= 1
            j -= 1
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值