Leetcode | 双指针 | Two Pointers[更新中]

3. 无重复字符的最长子串

在滑动窗口里面有写过这道题,当时使用hashtable来完成的;(2022-4-17)又做了一次,发现也是可以用双指针来做哈哈哈哈;其实sliding window和two pointers是有点像的。
搬运[stackoverflow的回答][https://stackoverflow.com/questions/64078162/is-two-pointer-problem-same-as-sliding-window]

  • Sliding window algorithms can be implemented with a single pointer and a variable for window size. Typically we use all of the elements within the window for the problem (for eg - sum of all elements in the window).
  • Two pointer technique is quite similar but we usually compare the value at the two pointers instead of all the elements between the pointers. Two pointers can also have variations like fast-slow pointer.
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if len(s) <= 1: return len(s) # 边界条件提前处理

        left, right = 0, 1
        ls = right - left
        while right < len(s):
        	# 如果先锋right和s[left: right](左闭右开)重复的话
        	# left需要跑到当前子串中和right重复的数值的后一位!
            if s[right] in s[left: right]:
                while s[left] != s[right]:
                    left += 1
                left += 1  # 跑到后一位
            right += 1     # 这里实际上是把
            ls = max(ls, right - left)
        return ls

167 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。

  • Solution:
    • 时间复杂度: O ( n ) O(n) O(n)
    • 空间复杂度: O ( 1 ) O(1) O(1)

划重点有序数组!

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left, right = 0, len(numbers) - 1
        while left < right:
            sum_tmp = numbers[left] + numbers[right]
            if sum_tmp == target:
                return [left+1, right+1]
            if sum_tmp < target:
                left += 1
            if sum_tmp > target:
                right -= 1

15. 三数之和

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

  • Solution: Sorting + Two pointers
    • 时间复杂度: O ( n l o g n + n 2 ) O(nlogn + n^2) O(nlogn+n2)
    • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if len(nums) < 3:
            return []

        nums.sort()  # !划重点!
        ans = []
        for i in range(len(nums)-2):
            # 去重1
            if i > 0 and nums[i] == nums[i-1]:
                continue
            j, k = i+1, len(nums)-1
            while j < k:
                sum_tmp = nums[j] + nums[k]
                if sum_tmp == -nums[i]:
                    ans.append([nums[i], nums[j], nums[k]])
                    k -= 1
                    # 去重2
                    while k > j and nums[k] == nums[k+1]:
                        k -= 1
                if sum_tmp < -nums[i]:
                    j += 1
                if sum_tmp > -nums[i]:
                    k -= 1
        return ans

16. 最接近的三数之和

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。

  • Solution:
    • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
    • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution:
    def threeSumClosest(self, nums: List[int], target: int):
        nums.sort()
        near = 10**8
        ans = target
        for i in range(len(nums)-2):
            if i > 0 and nums[i]==nums[i-1]:
                i += 1
            j, k = i+1, len(nums)-1
            while j < k:
                sum_tmp = nums[i]+nums[j]+nums[k]
                if near >= abs(sum_tmp - target):
                    ans = sum_tmp
                    near = abs(sum_tmp - target)

                if sum_tmp <= target:
                    j += 1
                if sum_tmp > target:
                    k -= 1
        return ans

11. 盛最多水的容器

掉到排序的🕳里了SAD
主要是指针更新的策略要在何时变,怎么变,会漏掉情况吗?

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。

  • Solution:双指针
    • 时间复杂度: O ( n ) O(n) O(n)
    • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution:
    def maxArea(self, height: List[int]):
        left, right, max_water = 0, len(height) - 1, 0
        while left < right:
            if height[left] < height[right]:
                max_water = max(max_water, (right - left) * height[left])
                left += 1
            else:
                max_water = max(max_water, (right - left) * height[right])
                right -= 1
        return max_water

42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

跟着花花酱的解题思路来:Brute Force–>DP–>Two Pointers

  1. 首先是暴力解;想法是考虑每一个柱子i的接雨水的情况:仅与其左右的最高柱子决定,即[0:i-1],[i+1:len(nums)]两个区域的高度最大值,柱子i接雨水的最大值为两个高度最大值的最小值减去柱子i本身的高度(短板性质!)(当然如果柱子本身的高度大于这个最小值,自然就是接不到雨水);
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)(每个柱子遍历&寻找左右最高柱子)
  • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution:
    def trap(self, height: List[int]) -> int:
        ## 边界处理:首个柱子和最后一个柱子不可能接到雨水
        ans = 0
        cur = 1 #边界1
        while cur < len(height) - 1: #边界2
            lmax = max(height[:cur])
            rmax = max(height[cur+1:])
            ans += max(min(lmax, rmax) - height[cur], 0)
            # if min(lmax, rmax) > height[cur]:
            #     ans += min(lmax, rmax) - height[cur]
            cur += 1
        return ans
  1. 第二个DP的想法实际上在Brute Force的基础上进行了时间复杂度的改进,当然是以空间换时间的。在Brute Force解法中,对于每个柱子我们都重新计算了一次该柱子左右两边的最高值(做很多比较,而这些比较做了很多重复),所以可以维护两个list分别存储该位置左边的最大值以及该位置右边的最大值;然后再让每个柱子按需取用;
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
class Solution:
    def trap(self, height: List[int]) -> int:
        lmax = [0] 
        rmax = [0] 
        ans = 0

        for i in range(1, len(height)): 
            lmax.append(max(lmax[i - 1], height[i - 1]))
            rmax.insert(0, max(rmax[0], height[len(height) - i]))
        
        for i in range(len(height)):
            ans += max(0, min(lmax[i], rmax[i]) - height[i])
            
        return ans
  1. 第三种做法是双指针;
    个人理解它不再是按柱子的顺序进行接雨水量的叠加了,而是一个动态的柱子雨水量的叠加;双指针的题目感觉就是在找某个条件,然后让左右指针移动;
    在这道接雨水的题目呢,两个指针 l e f t , r i g h t left,right left,right,我们同时记录 h e i g h t [ 0 : l e f t + 1 ] height[0:left+1] height[0:left+1]区间的最大值 l m a x lmax lmax以及 h e i g h t [ r i g h t : l e n ( h e i g h t ) ] height[right:len(height)] height[right:len(height)]的最大值 r m a x rmax rmax
    l m a x < r m a x lmax<rmax lmax<rmax时,此时我们可以确定第 l e f t + 1 left+1 left+1个柱子的最大接水量是 l m a x − h e i g h t [ l e f t + 1 ] lmax-height[left+1] lmaxheight[left+1],因为右侧的最大值 r e a l _ m a x real\_max real_max是确保 ≥ r m a x \geq rmax rmax,而接雨水是取左右最大值的最小值(即 l m a x < r m a x ≤ r e a l _ r m a x lmax<rmax\leq real\_rmax lmax<rmaxreal_rmax);
    同理当 l m a x > r m a x lmax>rmax lmax>rmax,此时我们可以确定第 r i g h t − 1 right-1 right1个柱子的最大接水量是 r m a x − h e i g h t [ r i g h t − 1 ] rmax-height[right-1] rmaxheight[right1]
    可以看到这个接水量不再是按照index的顺序来累加,而是取决于当前lmax和rmax的大小;
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

既然有了思路那么代码就比较好写啦!
只有两个柱子是接不了水的!其实自己写的边界需要控制;至于后面两个阵阵的循环条件中的等号的必要性是指针刚好相遇的时候这个地方是没有接过雨水的,(因为接过雨水的下一步就是挪动指针 没有进入循环就没法接雨水!)

class Solution:
    def trap(self, height: List[int]) -> int:
        if len(height) <= 2: return 0

        l, r = 1, len(height) - 2
        lmax, rmax = height[0], height[len(height) - 1]
        ans = 0

        while l <= r:
            if lmax < rmax:
                ans += max(0, lmax - height[l])
                lmax = max(lmax, height[l])
                l += 1
            else:
                ans += max(0, rmax - height[r])
                rmax = max(rmax, height[r])
                r -= 1
            #print(lmax, rmax, ans)
        return ans
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值