LeetCode | Two Pointers

之前开了一版,再来一次看看可以用到什么程度了

125. Valid Palindrome | Easy

ver1的思路很简单,check是不是字母数字,不是的话就移动指针;如果是的话就判断(字母统一转小写)是否是相同的char,不是的话就不是,是的话就移动指针继续check;

class Solution:
    def isPalindrome(self, s: str) -> bool:
        left, right = 0, len(s)-1

        def check(char):
            return (ord('a') <= ord(char) <= ord('z') or
                    ord('A') <= ord(char) <= ord('Z') or
                    ord('0') <= ord(char) <= ord('9'))

        while left < right:
            if check(s[left]) and check(s[right]):
                if s[left].lower() != s[right].lower():
                    return False
                else:
                    left += 1
                    right -= 1
            if not check(s[left]):
                left += 1
            if not check(s[right]):
                right -= 1
        return True

ver2是在check不是字母数字的时候做了优化,直接在当前while循环中找到合法的char的left & right位置,ps:顺序也重要hh

class Solution:
    def isPalindrome(self, s: str):
        left, right = 0, len(s) - 1
        while left <= right:
            while left < right and not self.helper(s[left]):
                left += 1
            while left < right and not self.helper(s[right]):
                right -= 1
            if s[left].lower() != s[right].lower():
                return False
            left += 1
            right -= 1
        return True

    def helper(self, char):
        return (ord('A') <= ord(char) <= ord('Z') or
                ord('a') <= ord(char) <= ord('z') or
                ord('0') <= ord(char) <= ord('9'))

167. Two Sum II - Input Array Is Sorted

  • ps:划重点已经排序!
    • left,right从起始位置0,len(numbers)-1出发,如果他们的和小于target,而此时right所指的是当前array的最大值,所以只能是left往右边挪;同理和大于target的情况;
    • 哈哈哈哈哈自己写的时候出错了看了一下是写了if,if,else,问题在最后一个else那里,他的else是离它最近的if条件的补集,不是我想要的前两个if条件的补集,应该改为if,elif,else;
class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left, right = 0, len(numbers) - 1
        while left < right:
            tmp = numbers[left] + numbers[right]
            if tmp == target:
                return [left + 1, right + 1]
            if tmp < target:
                left += 1
            if tmp > target:
                right -= 1

15. 3Sum

DFS | Fail

  • 救命 还以为dfs可以,还是超时了,不过记录一下去重的一点
    • 一开始我得去重写的是,这个问题就是,你不仅是把同层的重复元素剪枝了,还把下一层和构建到这个节点的路径上的元素重复的也剪掉了:举个例子[-1,-1,0]这个也被剪掉了,因为也会进入以下这个去重语句中continue掉了;
if i > 0 and nums[i] == nums[i-1]:
	continue
  • 既然我只想去重同层出现过的元素,下一层在[start:]index中选择不管是否和[:start]index中的元素有重复,那么也就是说[-1,-1]:第二个-1进来之后他的start是等于1的,他不会进入到去重判断语句中;

感觉还是没有讲清楚orz

if i > start and nums[i] == nums[i-1]:
	continue
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res, tmp = [], []
        nums = sorted(nums)

        def dfs(start, check):
            if len(tmp) == 3 and check == 0:
                res.append(tmp.copy())
                return

            for i in range(start, len(nums)):
                if i > start and nums[i] == nums[i-1]:
                    continue
                tmp.append(nums[i])
                check += nums[i]
                dfs(i+1, check)
                tmp.pop()
                check -= nums[i]
        dfs(0, 0)
        return res

排序+双指针 | Pass

  • 不仅仅是转到两数之和(167)的问题,由于这道题是需要返回所有的可能但是不能有重复;去重就是关键了;
    1. 首先是target的去重;
    2. fix target假设找到一个符合条件的解了,仍然还要继续搜索,此时left right要挪到与那个符合条件解不同的值为止,这里不用while会超时orz;
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums = sorted(nums)
        
        for i in range(len(nums) - 2):
            if i > 0 and nums[i] == nums[i-1]: # target去重
                continue

            left, right = i + 1, len(nums) - 1
            while left < right:
                tmp = nums[i] + nums[left] + nums[right]
                if tmp == 0:
                    res.append([nums[i], nums[left], nums[right]])
                    left += 1
                    while left < right and nums[left] == nums[left-1]: # 左加数去重
                        left += 1
                    right -= 1
                    while left < right and nums[right] == nums[right+1]: # 右加数去重
                        right -= 1
                if tmp < 0:
                    left += 1
                if tmp > 0:
                    right -= 1
        return res

11. Container With Most Water

  • 左右指针更新的问题:
    1. 当前左右指针的数值不等:好办;
    2. 当前左右指针的数值相等:为什么可以直接丢进上述1中的任意一个去做呢?
      • 当[left, right]内没有比当前大的值时,实际上你没必要再去while,最大值就是现在的res;
      • 当[left, right]内存在一个比当前值大时,实际上也没必要去while,有一个比他高的,但是次高的还是当前值,但是现在的宽度是比res小的,所以最大值还是现在的res;
      • 当[left, right]内存在不止一个当前值大时,这个时候是有可能出现比res大的值的,按照我们的策略(保留左右的max height)不管是left+=1,还是right-=1,他们都会在第一个(左和右)比当前值高的时候统一步调(后续同步除非又又遇到等高,碰到两个高高又统一,嗯), 即res相同了;
        在这里插入图片描述
class Solution:
    def maxArea(self, height: List[int]):
        res = 0
        left, right = 0, len(height)-1
        while left < right:
            res = max(res, (right - left) * min(height[left], height[right]))
            if height[left] < height[right]:
                left += 1
            else:
                right -= 1
        return res

42. Trapping Rain Water

  • 一开始想到的是遍历两次,一次for去把左起的最高值save下来,同时把右起的最高值save下来;第二次for去看看能接多少雨水,只有当前高度小于当前左右两边最高值的时候能接;
    在这里插入图片描述
  • 那么有没有简化的可能呢?
    • 双指针登场了!
    • 这里left和right什么时候选谁接雨水嘞?判断条件是lmax和rmax的大小,小的那边指针指的先接;
    • 为什么呢?因为实际上是对上面那个解法的一个简化:假设现在lmax<rmax,我们left想知道left前的最大值和left后的最大值,然后取这两最大值的最小值就是left水位最高处,简化做在哪里呢?实际上是在判断lmax<rmax的时候,我们知道从right位置之后的最高值是大于当前left之前的最大值的,也就是我们不用直到确切的两个最大值,我们只需要直到两个最大值的最小值,而left到right之间的值已经不需要check了,即left之后的最高值只会不小于当前的rmax,所以现在的left的最高水位就是lmax了;

还是没有讲顺的感觉orz

class Solution:
    def trap(self, height):
        if len(height) <= 2:
            return 0

        res = 0
        left, right = 1, len(height) - 2
        lmax, rmax = height[left - 1], height[right + 1]
        while left < right:
            if lmax < rmax:
                res += max(0, lmax - height[left])
                lmax = max(lmax, height[left])
                left += 1
            else:
                res += max(0, rmax - height[right])
                rmax = max(rmax, height[right])
                right -= 1
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值