python指针问题汇总

双指针问题

一.对撞指针

1.两数之和(有序数组)

问题描述:给定一个有序整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例

给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路
因为为有序数组,故可采用对撞指针的思想来解决问题。初始设置左、右两个指针,左指针为数组第一位,右指针为数组最后一位,target=左指针对应的值+右指针对应的值,比较target与目标值的大小,如果target>目标值,说明过大,则右指针往左移动一位,反之,过小,则左指针右移一位,当target=目标值或左右指针碰撞在一起时结束整个循环。

代码实现

from typing import List
def twoSum(nums: List[int], target: int) -> tuple:
    left = 0
    right = len(nums) - 1
    while left < right:
        sum = nums[left] + nums[right]
        if sum == target:
            return left, right
        else:
            if sum > target:
                right -= 1
            else:
                left += 1		
2.三数之和

问题描述:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。
示例

给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

思路
创建一个结果列表,先将数组排序,后固定一个元素,在该元素右边进行双指针的操作。
若三个元素的和小于0,则说明过小,左指针向右移动,若大于0,则过大,右指针往左移动,等于0,则插入结果。

代码实现

from typing import List
def threeSum(nums: List[int]) -> List:
    nums.sort()
    res = []
    for i in range(len(nums) - 2):
        left = i + 1
        right = len(nums) - 1
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        sum = nums[i] + nums[left] + nums[right]
        while left < right:
            if sum > 0:
                right -= 1
            elif sum < 0:
                left += 1
            else:
                res.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left + 1] == nums[right]:
                    left += 1
                while left < right and nums[right - 1] == nums[right]:
                    right -= 1
                left += 1
                right -= 1
    return res
3.最接近的三数之和

问题描述:给定一个包括 n 个整数的数组 nums和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

思路
指针的创建和三数之和类似。目的是找出三个元素的和与target最接近,即找出距离最近的即可。开始先指定一个最小距离值min=abs((nums[0]+nums[1]+nums[2])-target),结果res=nums[0]+nums[1]+nums[2],进入循环,先比较三个元素的和sum与target的距离是否比min小,若小,则更新min与res的值,若不小于,则再判断sum与target的大小,若小,则左指针向右移动,反之,右指针向左移动,若相等,则直接返回res。最后循环结束,返回res

代码实现

from typing import List
def threeSum_close(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):
        left = i + 1
        right = len(nums) - 1
        while left < right:
            sum = nums[i] + nums[left] + nums[right]
            if abs(sum - target) < min:
                min = abs(sum - target)
                res = sum
            if sum > target:
                right -= 1
            elif sum < target:
                left += 1
            else:
                return res
    return res
4.二分查找

问题描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

思路 1(对撞指针):设置两个指针作为边界,左边指针left设为-1,右边指针right设为len(nums),开始循环后计算中间值mid=(left+right)//2,判断目标值与mid对应的值的大小,若mid对应的值大于目标值,则右边界right的值赋为mid的值;若mid对应的值小于目标值,则左边界left的值赋为mid的值,若相等,则返回mid。

代码实现

from typing import List
def search(nums: List[int], target: int):
    left = -1
    right = len(nums)
    while right > 0:
        mid = (left + right) // 2
        if target not in nums:
            return -1
        if nums[mid] > target:
            right = mid
            print(right)
        elif nums[mid] < target:
            left = mid
            print(left)
        else:
            return mid

思路 2(递归思想):总体思想步骤与上述步骤相似,不过是当判断大小后,不再将左右指针直接重新赋值,而是再调用一次本身函数,在函数的参数里再给左右指针重新赋值。

代码实现

from typing import List
def binarySearch1(nums: List[int], left: int, right: int, val: int):
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] < val:
            return binarySearch1(nums, mid + 1, right, val)
        elif nums[mid] > val:
            return binarySearch1(nums, left, mid - 1, val)
        else:
            return mid
    return -1

二.快慢指针

1.删除排序数组中的重复项

问题描述:给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。

思路
创建两个指针,一个慢指针slow,一个快指针fast,slow记录新数组的长度,fast遍历原数组的元素。开始slow设为0,fast设为1,如果fast对应的元素与slow对应的元素相等,则fast向右移动一位,如果不等于,则slow先右移一位,再将fast的对应的值赋给slow对应的值,最后循环结束后,返回slow+1即为新数组的长度,原数组的前slow+1位即为去重后的数组。

代码实现

from typing import List
def remove_re(nums: List[int]) -> int:
    slow = 0
    fast = 1
    while fast < len(nums):
        if nums[fast] == nums[slow]:
            fast += 1
        else:
            slow += 1
            nums[slow] = nums[fast]
            fast += 1
    return slow + 1
2.移除元素

问题描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。

思路
创建两个指针分别为slow,fast,slow用来记录新数组的长度,fast用来遍历原数组。循环开始时先判断fast对应的值是否与要删除的目标值相同,若相同,则为应删除的目标,fast向右移动一位;若不同,则将fast对应的值赋给slow,然后slow与fast同时向右移动一位,直至循环结束。返回slow即为新数组的长度。

代码实现

from typing import List, Any
def remove(nums: List[Any], val: Any) -> int:
    slow = 0
    fast = 0
    while fast < len(nums):
        if nums[fast] == val:
            fast += 1
        else:
            nums[slow] = nums[fast]
            slow += 1
            fast += 1
    return slow
3.移动零

问题描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

思路
创建两个指针fast和slow,两指针的初始值都为0。开始循环后先判断fast对应的值是否为0,若是,则fast往后移动一位;若不相等,则将slow与fast对应的值交换位置,后fast和slow同时向后移动一位。

代码实现

from typing import List
def remove_zero(nums: List[int]) -> List:
    slow = 0
    fast = 0
    while fast < len(nums):
        if nums[fast] == 0:
            fast += 1
        else:
            nums[slow], nums[fast] = nums[fast], nums[slow]
            slow += 1
            fast += 1
    return nums

三.分离指针

1.两个数组的交集

问题描述:给定两个数组,编写一个函数来计算它们的交集。

示例

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

说明

  • 输出结果中的每个元素一定是唯一的。
  • 我们可以不考虑输出结果的顺序。

思路:先将两数组由小到大排序。创建两个指针,一个在nums1中移动,另一个再nums2中移动。将两指针代表的值进行比较,若nums1的值小于nums2的值,则nums1的指针右移一位,反之nums2的指针右移一位。当两者相等时,即为需要的值,将该值放入创建好的集合中,然后两指针同时右移一位。循环结束返回集合或列表。

代码实现

from typing import List
def intersect(nums1: List[int], nums2: List[int]) -> List:
    i = 0
    j = 0
    res = set()
    nums1.sort()
    nums2.sort()
    while i < len(nums1) and j < len(nums2):
        if nums1[i] < nums2[j]:
            i += 1
        elif nums1[i] > nums2[j]:
            j += 1
        else:
            res.add(nums1[i])
            j += 1
            i += 1
    return list(res)
2.合并两个有序数组

问题描述:给你两个有序整数数组 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]

思路:由于有两个数组,故也需要先设两个指针,初始位置分别为i=m-1与j=n-1,再设置一个指针k=m+n-1,用k来保存元素。循环开始,先将i与j对应的元素进行比较,若i对应的值大于等于j对应的值,则k对应的值赋为i对应的值,然后i与k向左移动一位,反之,k对应的值赋为j对应的值,然后j与k向左移动一位。循环结束后若j还大于0,则再进行循环,将k对应的值分别赋为j对应的值。最后得到nums1即为所要的结果。

代码实现

from typing import List
def merge_two_array(nums1: List[int], nums2: List[int], m: int, n: int):
    i = m - 1
    j = n - 1
    k = m + n - 1
    while j >= 0 and i >= 0:
        if nums1[i] >= nums2[j]:
            nums1[k] = nums1[i]
            k -= 1
            i -= 1
        else:
            nums1[k] = nums2[j]
            k -= 1
            j -= 1
    while j >= 0:
        nums1[k] = nums2[j]
        k -= 1
        j -= 1

问题来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problemset/all/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值