力扣算法面试150题——双指针——个人笔记复习用

双指针

第一题:

125. 验证回文串https://leetcode.cn/problems/valid-palindrome/

题目内容

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 

示例 1:
输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。

示例 2:
输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。

示例 3:
输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。

提示:

  • 1 <= s.length <= 2 * 105
  • s 仅由可打印的 ASCII 字符组成

思路

两个指针初始化,一根指向开头一根指向结尾。

若左指针对应内容不为单词,则右移1;右指针同理,若对应内容不为单词,则左移1

若开头等于结尾,则左边右移1,右边左移1。

结束判定:当左指针<右指针时

代码

class Solution:
    def isPalindrome(self, s: str) -> bool:
        # 先全部转小写
        s = s.lower()
        # 双指针
        i = 0
        j = len(s)-1

        while i<j:
            # 若当前指向内容不为字母,则移动指针
            if not s[i].isalnum():
                i += 1
                continue
            if not s[j].isalnum():
                j -= 1
                continue
            if s[i] != s[j]:
                return False 
            i += 1
            j -= 1

        return True

第二题:

392. 判断子序列https://leetcode.cn/problems/is-subsequence/

题目内容

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

示例 1:
输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:
输入:s = "axc", t = "ahbgdc"
输出:false

提示:

  • 0 <= s.length <= 100
  • 0 <= t.length <= 10^4
  • 两个字符串都只由小写字符组成。

思路

两个字符串s和t,分别用一根指针i和j。若i指向内容等于j指向内容,则i右移1,j右移1;若i指向内容不等于j指向内容,则i不动,j右移1。因此。分析发现,不管怎么样j都会右移1,而i不一定。

那么最终结束的判定条件就是i能否等于len(s),若等于,则True,否则False

附:也可以使用for循环的方式遍历t,然而时间复杂度更高,效率不及双指针。

代码

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        i = 0
        j = 0
        lengs = len(s)
        lengt = len(t)
        # while 循环判定范围
        while i<=lengs-1 and j<=lengt-1:
            if s[i] == t[j]:
                i += 1
            # j 指针不管怎么样都要右移
            j += 1
        # 结束条件
        if i == lengs:
            return True
        else:
            return False

“进阶”思路

目前的版本(双指针法)的时间复杂度是 O(∣s∣+∣t∣),如果有 K 个 s需要匹配,总时间复杂度就是 O(K⋅∣t∣)。当 K 很大且 t 很长时,每次都要遍历一遍 t,效率就很低了。

可以先对 t 进行一次预处理,建立一个“索引”。再用二分查找匹配,若能找到则更新位置,找不到则返回False

第三题

167. 两数之和 II - 输入有序数组https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/

题目内容

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1  index2

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。

示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

提示:

  • 2 <= numbers.length <= 3 * 104
  • -1000 <= numbers[i] <= 1000
  • numbers 按 非递减顺序 排列
  • -1000 <= target <= 1000
  • 仅存在一个有效答案

思路

一个数组,两个指针,分别指向头和尾,因为数组提前说好了,非递减顺序排列,且只对应唯一解。也就是说,右边的数字一定是大于等于左边的数字的。因此,若当前值>目标值,右指针左移1,同理,若当前值<目标值,那么左指针右移1。

代码

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        i = 0
        j = len(numbers) - 1
        while i < j:
            if numbers[i] + numbers[j] < target:
                i+=1
            elif numbers[i] + numbers[j] > target:
                j-=1
            else:
                return [i+1,j+1]

第四题

11. 盛最多水的容器https://leetcode.cn/problems/container-with-most-water/

题目内容

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:
输入:height = [1,1]
输出:1

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

思路

一个数组,两个指针,分别指向头和尾。

        能盛多少水,就看长 * 宽。长就是两个指针对应的内容的最小值min(height),宽就是两根指针的距离(j-i),更新条件就是判断左右的高低,左边低就往右移动(因为宽边小了,想要最终的面积更大,必须移动短的那一根,如果短的板不动的话,取到的水永远不会比上次多),同理右边低就往左移,知道i>=j结束。最终返回max_ans即为最大值。

代码

class Solution:
    def maxArea(self, height: List[int]) -> int:
        i = 0
        j = len(height)-1
        ans = 0
        while i<j:
            # 最终的面积
            ans = max(ans, min(height[i],height[j]) * (j-i))

            if height[i]<height[j]:
                i+=1
            else:
                j-=1
        return ans

第五题

15. 三数之和https://leetcode.cn/problems/3sum/

题目内容

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

思路

        先对整个列表从小到大排序,然后固定一个数i,对于剩下的两个数,双指针分别指向头j(i+1)和尾k(n-1)

先对i进行去重(第一次尝试写的时候犯的错,漏了 i,接着进行计算,若三数之和小于0,则左指针右移,因为j的左侧一定都比当前的值小;同理若三数之和大于0,则右指针左移,因为k的右侧一定都比当前的值大;若等于0,则记录答案。然后左右指针各自均移动。

        值得注意的是第一次尝试写的时候犯的错,在记录完答案并移动左右指针之后,还要对j、k去重。且这一步必须在记录完答案之后进行。否则会导致漏掉某些解。即去重逻辑应该是在找到一个有效三元组之后,为了寻找下一个可能解时才执行。在没有确定当前组合是否有效之前,不应该随意跳过指针。

代码

class Solution:
    def threeSum(self, nums: list[int]) -> list[list[int]]:
        # 先从小到大排序,便于后续使用双指针
        newnums = sorted(nums)
        n = len(nums)
        ans = []
        for i in range(n-2):
            # i 去重
            if i>0 and newnums[i] == newnums[i-1]:
                continue
            # 双指针分别指向头和尾
            j = i + 1
            k = n - 1

            while j<k:
                # 结果小于0,则左指针右移
                if newnums[i]+newnums[j]+newnums[k]<0:
                    j+=1
                # 结果大于0,则右指针左移
                elif newnums[i]+newnums[j]+newnums[k]>0:
                    k-=1
                else:
                    # 记录结果
                    ans.append([newnums[i], newnums[j], newnums[k]])
                    j+=1
                    k-=1
                    # 进一步j、k去重
                    while j<k and newnums[j] == newnums[j-1]:
                        j+=1
                    while j<k and newnums[k] == newnums[k+1]:
                        k-=1

        return ans

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值