leetcode 双指针

平方数之和 lc 633

给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c 。

class Solution:
    def judgeSquareSum(self, c: int) -> bool:
        for i in range(0,int(c**0.5)+1):
            for j in range(i,int(c**0.5)+1):
                if i**2+j**2==c:
                    return True
        return False

class Solution:
    def judgeSquareSum(self, c: int) -> bool:
        i,j=0,int(c**0.5)
        while i<=j:
            if i*i+j*j==c:
                return True
            elif i*i+j*j>c:
                j-=1
            elif i*i+j*j<c:
                i+=1
        return False
  • 下面的优化用二分的思路,第一个运算太多暴力穷举。
  • 最长上升子序列是每次都从前面看一遍,如果是每一个数和所有数的关系,是往后看。

最接近的三数之和 lc 16

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        n=len(nums)
        if(not nums or n<3):
            return None
        nums.sort()
        res=float("inf")
        for i in range(n-2):
        	#sort之后的剪枝操作,回溯也一样
            if(i>0 and nums[i]==nums[i-1]):
                continue
            L=i+1
            R=n-1
            while(L<R):
                cur_sum=nums[i]+nums[L]+nums[R]
                
                if(cur_sum==target):
                    return target
                if(abs(cur_sum-target)<abs(res-target)):
                    res=cur_sum
                if(cur_sum-target<0):
                    L+=1
                else:
                    R-=1
        return res

lc 18. 四数之和

在这里插入图片描述

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
    	if not nums or len(nums)<4:return []
        res = []
        nums.sort()
        for i in range(len(nums)-3):
            for j in range(i+1,len(nums)-2):
                left = j+1
                right = len(nums)-1
                while left<right:
                    cur_sum = nums[i]+nums[j]+nums[left]+nums[right]
                    if cur_sum==target:
                        tmp_lst =[nums[i],nums[j],nums[left],nums[right]]
                        if tmp_lst not in res:
                            res.append(tmp_lst)
                        right-=1
                        left+=1
                    elif cur_sum>target:
                        right=right-1
                    else:
                        left=left+1
        return res
  • 套路都是一样的,关键是后面有几个数不需要便利,比如本题第一个for后面有三个数,第二次for后面有两个数。
  • 有重复直接说not in或者in res就ok。

反转字符串中的元音字母 lc 345

class Solution:
    def reverseVowels(self, s: str) -> str:
        yuanyin = ['a','e','i','o','u','A','E','I','O','U']
        s_list = [i for i in s]
        i,j = 0,len(s_list)-1
        while i<j and i<len(s_list) and j>0:
            if s_list[i] not in yuanyin:
                i+=1
            if s_list[j] not in yuanyin:
                j-=1
            if s_list[i] in yuanyin and s_list[j] in yuanyin:
                s_list[i],s_list[j] = s_list[j],s_list[i]
                i+=1
                j-=1
        return ''.join(s_list)

    def reverseVowels(self, s: str) -> str:
        l = len(s)
        s = [char for char in s]
        vowel = ["a", "e", "i", "o", "u", "A", "E", "I", "O", "U"]
        left = 0
        right = l - 1
        while(left < right):
            while(left <= l -1 and s[left] not in vowel):
                left += 1
            while(right >= 0 and s[right] not in vowel):
                right -= 1
            #这个位置必须判断,否则会内部越界
            if left < right:
                s[left], s[right] = s[right], s[left]
            	left += 1
            	right -= 1
        return "".join(s)
  • 这是经典的写法,包括快排。

验证回文字符串 Ⅱ lc 680

最多只删除一次,判断是否是回文串,如果原来就是也可以。

class Solution:
    def validPalindrome(self, s: str) -> bool:
        def isPalindrome(s,start,end):
            while start < end:
                if s[start] == s[end]:
                    start+=1
                    end-=1
                else:
                    return False
            return True

        i = 0
        j = len(s)-1
        while i<j:
            if s[i]==s[j]:
                i+=1
                j-=1
            else:
                return isPalindrome(s,i+1,j) or isPalindrome(s,i,j-1)
        return True
  • 回文是一个相同的操作首尾的走,但是判断一次当时想的是设置一个flag,但是不太好构思后面的。
  • 于是乎,其实错开位置再执行相同的内容,也别非去递归了,如上所示就可以。多看看,借鉴这个思路。
  • 这个题目好在形式上看出来后无效性,dp,直接舍弃两边成功的,看中间的。

颜色分类 lc 75

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
#p,k分别指向首尾,q是移动指针,要移动的那个指针和尾部的去查
        p,q=0,0
        k=len(nums)-1

        while q<=k:
            if p<q and nums[q] == 0:
                nums[p],nums[q] = nums[q],nums[p]
                p+=1
            elif nums[q]==2:
                nums[q],nums[k] = nums[k],nums[q]
                k-=1
            else:
                q+=1
  • 这个题是三个指针。
  • 快速的在范围内查找。
  • 不改变地方,原地的改变位置。

删除排序后重复的数

lc 26. 删除有序数组中的重复项

每个元素最多出现一次
在这里插入图片描述

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        for i in range(len(nums)):
            left = i
            right = i
            while right<len(nums) and nums[left]==nums[right]:
                right+=1
                while right<len(nums) and right-left>=1 and nums[left]==nums[right]:
                    nums.pop(left)
                    right-=1

lc 80. 删除有序数组中的重复项 II

每个元素最多出现两次,但是其实我的框架可以最多出现k次都没所谓,很明显的双指针

在这里插入图片描述

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        for i in range(len(nums)):
            left = i
            right = i
            while right<len(nums) and nums[left]==nums[right]:
                right+=1
                while right<len(nums) and right-left>=2 and nums[left]==nums[right]:
                    nums.pop(left)
                    right-=1

lc 42 接雨水

在这里插入图片描述

class Solution:
    def trap(self, height) -> int:
        # 边界条件
        if not height: return 0
        n = len(height)
        maxleft = [0] * n
        maxright = [0] * n
        ans = 0
        # 初始化
        maxleft[0] = height[0]
        maxright[n-1] = height[n-1]
        # 设置备忘录,分别存储左边和右边最高的柱子高度
        for i in range(1,n):
            maxleft[i] = max(height[i],maxleft[i-1])
        for j in range(n-2,-1,-1):
            maxright[j] = max(height[j],maxright[j+1])
        # 一趟遍历,比较每个位置可以存储多少水
        for i in range(n):
            if min(maxleft[i],maxright[i]) > height[i]:
                ans += min(maxleft[i],maxright[i]) - height[i]
        return ans

# 自然的想法
    def trap(self, height) -> int:
        ans = 0
        for i in range(len(height)):
            max_left, max_right = 0, 0
            # 寻找 max_left
            for j in range(0, i):
                max_left = max(max_left, height[j])
            # 寻找 max_right
            for j in range(i, len(height)):
                max_right = max(max_right, height[j])
            if min(max_left, max_right) > height[i]:
                ans += min(max_left, max_right) - height[i]
        return ans
  • 比较经典的题,如果是单调栈比较不好想,从中间往两边找,找到当前位置的最大值就找两边的小值之差就是能存住的雨水量。

lc 84 直方图的面积

在这里插入图片描述

class Solution:
    def largestRectangleArea(self, heights) -> int:
        res = 0
        n = len(heights)
        for i in range(n):
            left_i = i
            right_i = i
            while left_i >= 0 and heights[left_i] >= heights[i]:
                left_i -= 1
            while right_i < n and heights[right_i] >= heights[i]:
                right_i += 1
            res = max(res, (right_i - left_i - 1) * heights[i])
        return res

    def largestRectangleArea(self, heights: List[int]) -> int:

        maxarea = 0
        stk = list()

        for i in range(len(heights)):
            # 只要栈不为空,并且当前遍历的数据小于栈顶元素,则说明找到了右边界
            while stk and heights[i] < heights[stk[-1]]:
                h = heights[stk[-1]];
                stk.pop()
                # 出栈后,如果栈为空,说明出栈的柱子目前遍历的最短的柱子,否则计算宽度差
                w = i - stk[-1] - 1 if stk else i
                maxarea = max(h * w, maxarea)
            # 栈为空或者当前遍历的数据大于等于栈顶数据,则入栈,不会执行上面的while循环
            # 保证了栈中的数据总是递增的 比如  0 1 2 2 3 4 4 5 6 ...
            stk.append(i)

        # 处理剩余栈中的数据(递增的数据,右边界是柱状图中最右边的柱子)
        while stk:
            h = heights[stk[-1]]
            stk.pop()
            # 出栈后,如果栈为空,说明出栈的柱子目前遍历的最短的柱子,否则计算宽度差
            w = len(heights) - stk[-1] - 1 if stk else len(heights)
            maxarea = max(h * w, maxarea)

        return maxarea
  • 单调栈还是不好想,基本的想法是:说从每一个柱子遍历,如果两边有比自己大,那用自己的高度✖️比他高的跨度。这是一定满足条件的,整体的遍历滚动起来则覆盖了所有的内容。
  • 单调栈则是维护了一个单调递增的栈,计算当前。
  • 这个题和接雨水不同的是接雨水是远端一直的最大值是目标。这个题好是求面积所以是说临近的比他小就停下了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值