双指针 double pointer

本文介绍了双指针技巧在解决LeetCode问题中的应用,主要分为快慢指针和碰撞指针两种类型。快慢指针用于原地修改数组,如移除元素、删除重复项、移动零等;碰撞指针常用于字符串翻转和数组操作,如有序数组的平方、反转字符串等。通过案例解析了每种类型的典型解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

双指针的题目一般分为2种,一种是类似龟兔赛跑的快慢指针,2个指针同时从头出发;另一种是类似左右指针的碰撞指针,2个指针分别从头尾出发,直到相撞

快慢指针/原地修改:快慢指针的2个指针从开始位置一起出发,fastp用于遍历数组中每个元素,slowp用于指向数组中待赋值的位置,并等待fastp寻找到符合要求的给其赋值,最后slowp指向的位置之前的就是满足题意的部分;如果不是原地修改,则2个指针也是从2个变量的各自位置出发,具体问题具体分析,没有统一的套路/模板

碰撞指针:左右指针分别指向数组的首尾位置,依次寻找满足条件的位置,并移动指针,一般来说这种题目都会涉及到反转/翻转字符串/数组

需要强调的是,虽然双指针有各种分类,但是万变不离其宗,本质上都是通过指针遍历的方式遍历数组/字符串,如果是快慢指针则1个指针遍历1个指针辅助记录满足条件的位置,如果是碰撞指针则2个指针都用来遍历数组/字符串(没有速度的概念),直到2个指针相遇

快慢指针

27 移除元素

  • easy
  • 题目描述

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

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

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

  • 解题思路
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slowp = 0
        fastp = 0
        for fastp in range(len(nums)):
            # fastp会一直向后面寻找到不等于val的元素,此时slow指向等于val的位置
            if nums[fastp] != val:
                nums[slowp] = nums[fastp]
                slowp += 1

        # slow指向等于val的位置,因为返回长度,所以不需要-1
        return slowp

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

  • easy
  • 题目描述

给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

  • 解题思路
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        if len(nums) <= 1:
            return len(nums)

        slowp = 0
        for fastp in range(1, len(nums)):
            if nums[fastp] == nums[slowp]:
                continue
            else:
                slowp += 1
                nums[slowp] = nums[fastp]

        return slowp + 1

283 移动零

  • easy
  • 题目描述

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

  • 解题思路
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        fastp = 0
        slowp = 0
        for fastp in range(len(nums)):
            if nums[fastp] == 0:
                continue
            else:
                nums[slowp] = nums[fastp]
                slowp += 1
        # 最后把剩余部分更新为0
        nums[slowp:len(nums)] = [0 for i in range(len(nums)-slowp)]

844 比较含退格的字符串

  • easy
  • 题目描述

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。# 代表退格字符。

如果相等,返回 true ;否则,返回 false 。

注意:如果对空文本输入退格字符,文本继续为空。

  • 解题思路
class Solution:
    def backspaceCompare(self, s: str, t: str) -> bool:

        # 返回字符串对应的真实字符串
        def trueString(s:str) -> str:
            # slow记录真实字符串
            slowp = 0
            fastp = 0
            # res = ""
            # 原地修改
            for fastp in range(len(s)):
                if s[fastp] == '#':
                    slowp = slowp - 1 if slowp > 1 else 0
                    # res = s[:slowp]
                else:
                    # 把slowp位置替换成fastp(同时保持字符串长度不变)
                    s = s[:slowp] + s[fastp] + s[slowp+1:]
                    slowp += 1
            return s[:slowp]

        print(trueString(s))
        print(trueString(t))
        return trueString(s) == trueString(t)

剑指offer05 替换空格

  • easy
  • 题目描述

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

  • 解题思路
class Solution:
    def replaceSpace(self, s: str) -> str:
        # 双指针,统计空格数量,然后从后向前替换
        count = 0
        for i in s:
            if i == " ":
                count += 1
        
        # 原始字符串长度
        m = len(s)
        # 替换以后字符串的长度
        s += " "*(2*count)
        # 字符串转成字符数组
        res = list(s)
        n = len(s)
        i = m - 1
        j = n - 1
        while(i >= 0 and j > i):
            if res[i] == " ":
                res[j] = "0"
                res[j-1] = "2"
                res[j-2] = "%"
                j -= 3
            else:
                res[j] = res[i]
                j -= 1
            i -= 1
        return "".join(res)

碰撞指针

977 有序数组的平方

  • easy
  • 题目描述

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

  • 解题思路
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 最大值一定产生于边界位置
        leftp = 0
        rightp = len(nums) - 1
        res = [0 for i in range(len(nums))]
        count = rightp
        while(leftp <= rightp):
            leftv = nums[leftp] * nums[leftp]
            rightv = nums[rightp] * nums[rightp]
            if rightv >= leftv:
                res[count] = rightv
                count -= 1
                rightp -= 1
            else:
                res[count] = leftv
                count -= 1
                leftp += 1
        return res

344 反转字符串

  • easy
  • 题目描述

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

  • 解题思路
class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        left = 0
        right = len(s) - 1
        while(left < right):
            s[left], s[right] = s[right], s[left]
            left += 1
            right -= 1

541 反转字符串Ⅱ

  • easy
  • 题目描述

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

  • 解题思路
class Solution:
    def reverseStr(self, s: str, k: int) -> str:
        # 反转一个完整的字符串&确定每次反转的部分字符串

        # 给定部分字符串,返回该字符串反转后的结果
        def reverseAll(s: str) -> str:
            left = 0
            right = len(s) - 1
            res = list(s)
            while(left < right):
                res[left], res[right] = res[right], res[left]
                left += 1
                right -= 1
            return "".join(res)

        p = 0
        # 字符串转成列表,方便字符串的修改和赋值
        res = list(s)
        while(p!=len(s)):
            if p+k < len(s):
                res[p:p+k] = reverseAll(s[p:p+k])
            else:
                res[p:] = reverseAll(s[p:])
                return "".join(res)
            p = p+k*2
        return "".join(res)

151 翻转字符串里的单词

先反转整体,再反转局部

  • medium
  • 题目描述

给你一个字符串 s ,逐个翻转字符串中的所有单词 。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

说明:

输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。

  • 解题思路
class Solution:
    def reverseWords(self, s: str) -> str:
        # 先反转整个字符串,然后逐个反转单词

        # 反转字符串
        def reverseAll(s):
            left = 0
            right = len(s) - 1
            res = list(s)
            while(left < right):
                res[left], res[right] = res[right], res[left]
                left += 1
                right -= 1
            return res

        # 去除原始字符串中的首尾空格
        left = 0
        while(s[left] == " "):
            left += 1
        right = len(s) - 1
        while(s[right] == ""):
            right -= 1
        s_temp = reverseAll(s[left:right+1])
        res = s_temp

        # 反转逐个单词
        left = 0
        right = 0
        # 标志前面是否已经找到一个单词,跳过长段空格
        flag = 0
        ans = ""
        while(right != len(res)):
            # 如果直接写if res[right]:这样是不对的,因为空格也会进入这个分支里
            if res[right] != " ":
                right += 1
                flag = 1
            # 碰到空格
            else:
                if flag == 1:
                    ans += "".join(reverseAll(res[left:right]))
                    ans += " "
                    # 前面的单词已经记录过了
                    flag = 0
                    right += 1
                    left = right
                else:
                    right += 1
                    left = right
        # 末尾没有空格了,所以最后一个单词需要手动添加
        ans += "".join(reverseAll(res[left:right]))
        return ans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值