python 双指针_双指针应用二(python)

二,对撞指针

对撞指针:在有序数组中,将指向最左侧的索引定义为左指针(left),最右侧的定义为右指针(right),然后从两头向中间进行数组遍历。

1,两数之和

力扣01-题目: 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

来源:力扣(LeetCode)链接:力扣

思路:由题要求知为有序数组,即可使用双指针,一个指针指向值较小的元素(left),一个指针指向值较大的元素(right)形成对撞之势。

左指针从头向尾遍历,右指针从尾向头遍历。如果两个指针指向元素的和(sum)与目标值(target)相等,那么得到要求的结果;如果 sum > target,右指针移动,使 sum变小一些;如果 sum < target,左指针移动,使 sum变大一些。

重复上述循环直到找到答案输出。

复杂度分析: 时间复杂度:O(N) , 因为 每个元素最多被访问一次,共有 n个元素。空间复杂度:O(1), 指针使用常数大小的额外空间。

class Solution:

def twoSum(self, nums: List[int], target: int) -> List[int]:

nums_dict={}

for i in range(len(nums)):

temp=target-nums[i]

if temp in nums_dict:

return [i,nums_dict[temp]]

else:

nums_dict[nums[i]] = i

2,三数之和

力扣015-题目: 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。

来源:力扣(LeetCode)链接:力扣

思路:本题的难点在于如何去除重复解。首先需要对数组进行排序, 并且先固定一个中心值 nums[i], 然后应用左右双指针指向nums[i]的两端, 交替向中间移动遍历排序后的数组。

如果 nums[i]>0:因为数组升序排序,所以后面不可能有三个数加和等于 0,因此直接返回结果。

对于重复元素通过双指针可加1减1跳过,避免出现重复解。左指针 Left=i+1,右指针 Right=n-1,当 L

当 nums[i]+nums[L]+nums[R]==0 执行循环,判断左界和右界是否有下一位重复值,去除重复。并同时将 L,R移到下一位置,寻找新的解若和大于 0,说明 nums[R]太大,R左移; 若和小于 0,说明 nums[L] 太小,L 右移。

题目要求返回三元组, 即循环前要定义一个空列表用于记录每个符合条件的三元组。

复杂度分析, 时间复杂度:O(N^2), 进行了一次循环; 空间复杂度 O(1):指针使用常数大小的额外空间。

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

if nums[i]+nums[left]+nums[right] > 0:

right -=1

elif nums[i]+nums[left]+nums[right] < 0:

left +=1

else:

res.append([nums[i],nums[left],nums[right]])

while left

left +=1

while left

right -=1

left +=1

right -=1

return res

3,最接近的三数之和

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

来源:力扣(LeetCode)链接:力扣

思路:题目要求找到与目标值 target最接近的三元组,这里的「最接近」即为差值的绝对值最小

大体思路同上, 首先排序。因为返回的是和即不需要一个空列表, 首先设定初始值与最小差值的绝对值res=nums[0+nums[1]+nums[2], min=abs(res-target)

通过循环, 不断比较sum ( sum=nums[i]+nums[left]+nums[right]) 与 target 的差值的绝对值和min的大小是否比之前的更小了,如果是,更新res。

循环中 sum > target,右指针移动减1,sum < target,左指针移动加1,最后返回值。

复杂度分析: 时间复杂度: O(N^2), 进行了一次循环; 空间复杂度: O(1)

class Solution:

def threeSumClosest(self, nums: List[int], target: int) -> int:

nums.sort()

res=nums[0]+nums[1]+nums[2]

min=abs(res-target)

for i in range(len(nums)-2):

left=i+1

right=len(nums)-1

while left

sum=nums[i]+nums[left]+nums[right]

if abs(sum-target)

min=abs(sum-target)

res=sum

if sum>target:

right -=1

elif sum

left +=1

else:

return sum

return res

4,二分查找

是一种基于比较目标值和数组中间元素的教科书式算法。如果目标值等于中间元素,则找到目标值。如果目标值较小,继续在左侧搜索。如果目标值较大,则继续在右侧搜索。

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

来源:力扣(LeetCode)链接:力扣

思路:初始化指针 left = 0, right = n 。

当 left <= right:目标值 target 不在 nums 中返回-1。

目标值 target 在 nums 中, 比较中间元素 nums[mid] 和目标值 target 。如果 target = nums[mid],返回 mid。如果 target < nums[mid],则在左侧继续搜索 right = mid。如果 target > nums[mid],则在右侧继续搜索 left = mid, 重复循环得出结果。

复杂度分析: 时间复杂度:O(logN); 空间复杂度:O(1)。

class Solution:

def search(self, nums: List[int], target: int) -> int:

left = 0

right = len(nums)

while left <= right:

if target not in nums:

return -1

mid = (left+right)//2

if target < nums[mid]:

right = mid

elif target > nums[mid]:

left = mid

else:

return mid

三,分离指针

分离指针:是指两个指针分别在两个数组中移动,根据问题的不同,初始位置可能都在头部,或者都在尾部,或一头一尾。

1,两个数组的交集

力扣349-题目: 给定两个数组,编写一个函数来计算它们的交集。说明:输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序。

来源:力扣(LeetCode)链接:力扣

思路:先将 nums1 与 nums2 排序,然后通过两个指针遍历数组,比较 nums1[i] 与 nums2[j] 谁小就往前走直到相等, 将 nums1[i] 增加到一个空的集合里, 最后返回这个集合。

复杂度分析: 时间复杂度:O(NLogN) ; 空间复杂度:O(m+n)。

class Solution:

def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:

nums1.sort()

nums2.sort()

i,j=0,0

nume_set=set()

while i

if nums1[i]>nums2[j]:

j +=1

elif nums1[i]

i +=1

elif nums1[i]==nums2[j]:

nume_set.add(nums1[i])

i +=1

j +=1

return list(nume_set)

2,合并两个有序数组

力扣088-题目: 给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。说明: 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

来源:力扣(LeetCode)链接:力扣

思路:从后向前遍历数组改变 nums1 的值不需要额外空间。

这里的指针 k 用于追踪添加元素的位置和控制输出数组的大小。

因为 nums1 的空间都集中在后面,所以从后向前处理排序的数据会更好,节省空间,一边遍历一边将值增加进去。

设置指针 i 和 j 分别指向 nums1 和 nums2 的有数字尾部,从尾部值开始比较遍历,同时设置指针 k 指向 nums1 的最末尾,每次遍历比较值大小之后,则进行赋值。

当 k <0 时遍历结束,此时 nums1 或 nums2 中还有一个头部数据未拷贝完全,再进行一次判断, 将其直接赋值到 nums1 的前面,最后得到结果数组。

复杂度分析: 时间复杂度 : O(n + m); 空间复杂度 : O(1)。

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

elif nums1[i] < nums2[j]:

nums1[k] = nums2[j]

j -= 1

k -= 1

while i>=0:

nums1[k]=nums1[i]

i -=1

k -=1

while j>=0:

nums1[k]=nums2[j]

j -= 1

k -= 1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值