双指针法(数组)-python

文章介绍了双指针法的概念和优势,通过四个具体的LeetCode问题(移除元素、有序数组的平方、移动零、删除有序数组中的重复项)的解题思路,阐述如何利用快慢指针在O(n)的时间复杂度内解决问题,强调了这种方法的巧妙性和实用性。
摘要由CSDN通过智能技术生成


前言

双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。双指针时间复杂度是O(n)。双指针法在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,均使用双指针法解决。


一、思路

首先定义双指针(快慢指针)
快指针:寻找新数组的元素,新数组就是不含有目标元素的数组
慢指针:指向更新新数组的下标
大家一定要理解这里的快慢指针,后面的题就好套用思路来解了。

slow=0
for(fast=0;fast<nums.size;fast++){
	if(nums[fast]!=val){#快指针指向我们新数组所需要的元素
	#当快指针所指向的元素不等于我们要删除的元素
	#将快指针赋给慢指针,即我们新数组所需要的元素赋给slow
		nums[slow++]=nums[fast];#说白了就是把快指针指向的值赋到我们这个新数组所对应的下标的位置
		#slow++;
	}
}
return slow;

二、相关题目解读

1.移除元素(leetcode 27)

代码如下(示例):

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        length=len(nums)
        slow=0
        for fast in range(length):
            if nums[fast]!=val:
                nums[slow]=nums[fast]
                slow+=1
        return slow

时间复杂度O(n),空间复杂度O(1).这个快慢指针法仔细体会,真的非常的巧妙。实际就是遇到非目标值,直接跳过去,未必一定要附值,slow表示了新数组的下标。

2.有序数组的平方(leetcode 977.)

vector<int>result
k=numsize-1
for(i=0;j=numsize-1;i<=j; )
	if (nums[i]^2>nums[j]^2):
		result[k]=nums[i]^2;
		k--;
		i++;
	else{
		result[k--]=nums[j]^2;
		j--;
	}
	return result;

具体用Python实现代码如下

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:        
        n=len(nums)
        ans=[0]*n
        i,j,pos=0,n-1,n-1
        while i<=j:
            if nums[i]*nums[i]>nums[j]*nums[j]:
                ans[pos]=nums[i]*nums[i]
                i+=1
            else:
                ans[pos]=nums[j]*nums[j]
                j-=1
            pos-=1
        return ans

这道题,一开始想的解决方法是,对有序数组平方后,再使用快速排序法重新排成由小到大的有序数组,快排的时间复杂度为O(nlogn).本题使用快慢指针法,首先分析这个有序数组,假如是[-5,1,2,3],我们会发现平方后的数组,一定是两边大中间小的数组。
我们可以使用两个指针分别指向位置0和 n−1,每次比较两个指针对应的数,选择较大的那个逆序放入答案并移动指针。这种方法无需处理某一指针移动至边界的情况,大家可以仔细思考其精髓所在。

3. 移动零(leetcode 283.)

代码如下(示例):

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        if not nums:
            return 0
        n=len(nums)
        left=0#左指针指向当前已经处理好的序列的尾部
        right=0#右指针指向待处理序列的头部
        while right<n:
            if nums[right]!=0:
                nums[left],nums[right]=nums[right],nums[left]
                left+=1
            right+=1

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意:左指针左边均为非零数;右指针左边直到左指针处均为零。
因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。算法算法时间复杂度O(n),其中n是数组的长度,每个位置至多被遍历两次;空间复杂度O(1),只需要使用常数的额外空间。

4.删除有序数组中的重复项(leetcode 26.)

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        #快慢指针法
        if not nums:
            return 0
        n=len(nums)
        fast=slow=1
        while fast<n:
            if nums[fast]!=nums[fast-1]:#唯一元素判断
                nums[slow]=nums[fast]
                slow+=1
            fast+=1
        return slow#然后返回 nums 中唯一元素的个数。

算法时间复杂度O(n),其中n是数组的长度,快慢指针最多各移动n次;空间复杂度O(1),只需要使用常数的额外空间。


总结

以上就是双指针法在数组中的应用举例,通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。实现了将时间复杂度O(n2)的暴力解法降为O(n),如果没有接触过这一类的方法,很难想到类似的解题思路,双指针方法还是很巧妙的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值