【leetCode力扣热题100(二)——双指针】适合入门·Python代码及详细解析(持续更新)

目录

283.移动零

11.盛水最多的容器 

15.三数之和

42.接雨水 


283.移动零

# 自己写的
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        b = 0
        # a是快指针,b为满指针,都从0位置开始
        for a in range(len(nums)):
            if nums[b] == 0: # 检查b指针位置的元素是否为零
                if nums[a] != 0:    #检测a指针的位置是否不是零,不是的话则可以交换
                  nums[a], nums[b] = nums[b], nums[a]
                  b += 1
                else:
                    continue # b为0,a也为0,则a继续寻找下一个不为零的数
            else:
                b +=1 #b不为0,b寻找直到下一个0
  1. 我感觉这个代码的逻辑还比较好懂的,就是让b在每一个0的位置等候直到a找到最近的不为0的数,然后a和b所指的数值交换,a和b同时往后移。因为一开始二者是一起出发的,所以运行时b~a-1的位置应该都是0。

逻辑点 

  1. 排序算法。像这种涉及到数组里面各个元素换位置的,基本都用到了排序算法,建议自己去学一下。这道题就用到了快速排序里面的交换位置的思想。
class Solution(object):
	def moveZeroes(self, nums):
		"""
		:type nums: List[int]
		:rtype: None Do not return anything, modify nums in-place instead.
		"""
		if not nums:
			return 0
		# 两个指针i和j
		j = 0
		for i in xrange(len(nums)):
			# 当前元素!=0,就把其交换到左边,等于0的交换到右边
			if nums[i]:
				nums[j],nums[i] = nums[i],nums[j]
				j += 1

# 来源https://leetcode.cn/problems/move-zeroes/solutions/90229/dong-hua-yan-shi-283yi-dong-ling-by-wang_ni_ma/

逻辑点: 

  1. 我初看这个代码的时候很疑惑为什么没有判断慢指针所指是否为0就交换了,后来才意识到,快慢指针是从同一位置开始的,所以说一开始两者所指的数是一样的,所以一开始进行的交换不会影响整体顺序。满足 if nums[i] 条件,即快指针不为0时,两者同增,自己交换自己;不满足 if nums[i] 条件,即快指针为0,那么快指针就会先走一步,这时候慢指针指向这个0而快指针指向慢指针+1。后面碰到更多的0,快指针也总是会跳过。

11.盛水最多的容器 

class Solution:
    def maxArea(self, height: List[int]) -> int:
        i, j, res = 0, len(height) - 1, 0
        while i < j:
            if height[i] < height[j]:
                res = max(res, height[i] * (j - i))
                i += 1
            else:
                res = max(res, height[j] * (j - i))
                j -= 1
        return res

# 来源:https://leetcode.cn/problems/container-with-most-water/solutions/11491/container-with-most-water-shuang-zhi-zhen-fa-yi-do/
  1.  i是左指针,j是右指针,res是盛水面积。我们肯定不会想暴力搜索来解决这个问题,我们考虑一件事,就是方形的面积是由底乘高决定,对于高,水桶的容量由最短的一根木板决定,也就是说,如果a比b短,即使b找到了更高的也没有用,所以此时没有必要去移动b,而应该移动a;对于底,当我们移动指针寻找更高的木板时,底的长度会变化,我们当然是希望底越大越好,所以,我们从最大的底(0和length-1)开始,向中间收拢。当前一次只移动最短木板的指针。注意要“全部搜完”也就是两边指针相遇才能结束,而不能这次max没有变得更大就结束了,因为两根木板中间可能有更长的两根木板使得方形面积大于当前面积。

15.三数之和

class Solution:
    def threeSum(self, nums: [int]) -> [[int]]:
        nums.sort()
        res, k = [], 0
        for k in range(len(nums) - 2):
            if nums[k] > 0: break # 1. because of j > i > k.
            if k > 0 and nums[k] == nums[k - 1]: continue # 2. skip the same `nums[k]`.
            i, j = k + 1, len(nums) - 1
            while i < j: # 3. double pointer
                s = nums[k] + nums[i] + nums[j]
                if s < 0:
                    i += 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                elif s > 0:
                    j -= 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
                else:
                    res.append([nums[k], nums[i], nums[j]])
                    i += 1
                    j -= 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
        return res

# 来源:https://leetcode.cn/problems/3sum/solutions/1/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/
  1.  nums.sort(): 将数组排序,这一点很重要,可以大大减少后面的工作量,一方面它方便我们跳过重复数值(排完序之后一样的数挨在一起),另一方面它用0区分了整数和负数,方便我们排除不可能的情况(比如三个数都大于0就没有继续找的必要了)。
  2. k,i,j:k是从左往右进行for循环用的,i 被设置为 k+1,j 被设置为数组末端,每次循环中通过调整 i 和 j 来找和为0的组合。
  3. if nums[k] > 0: break:因为 j > i > k,所以nums[j] >= nums[i] >= nums[k] > 0,即 3 个元素都大于 0 ,不可能有和为0的情况了。

  4. if k > 0 and nums[k] == nums[k - 1]: continue:因为已经将 nums[k - 1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合。

  5. 当找到三数和为0时:记录组合[k, i, j]至res。注意i += 1和j -= 1继续往中间找,不能直接退出了,还要跳过重复的nums[i]和nums[j],防止记录到重复组合。

逻辑点

  1. 怎么想到这样设置指针?我观察了一下好像双指针类型的题,如果真的只需要两个指针(比如11题,要么是从左往右嵌套循环(暴力解法一般不用)要么是排序后从两端向中间;要么是三个“指针”(比如这题还有一个k),就以k做外层循环,然后内部两个指针参照双指针解法。
  2. 核心思想是排序,排序之后就能用0区分正数和负数,可以省很多事。下面给你们看一下我的解法,跟这个逻辑差不多,但是少考虑了几个冗余点,导致时间复杂度一直过不了:
class Solution:
    def threeSum(self, nums: list[int]) -> list[list[int]]:
        result = [] # 没有给数组排序,考虑不了三数大于零的情况
        for i in range(len(nums)-2):
            j = i + 1 # 没有考虑重复数字的情况
            while j < len(nums)-1:
                c = 0 - nums[i] - nums[j]
                for k in range(j + 1, len(nums)):
                    if nums[k] == c:
                        a = sorted([nums[i], nums[j], c]) #在这里排序是为了答案中消除数字一样但顺序不一样的组合,但是如果在一开始直接排序,就用循环套排序了
                        if a not in result:
                            result.append(a)
                        break
                j += 1
        return result

42.接雨水 

class Solution:
    def trap(self, height: List[int]) -> int:
        if not height:
            return 0
        
        left, right = 0, len(height) - 1
        left_max, right_max = height[left], height[right]
        water_trapped = 0
        
        while left < right:
            if height[left] < height[right]:
                left += 1
                left_max = max(left_max, height[left])
                water_trapped += left_max - height[left]
            else:
                right -= 1
                right_max = max(right_max, height[right])
                water_trapped += right_max - height[right]
        
        return water_trapped
  1. 双指针法:我们使用两个指针,分别从数组的两端开始,向中间移动。左指针从左向右移动,右指针从右向左移动。

  2. 维护最大高度:在移动指针的过程中,我们维护两个变量 left_max 和 right_max,分别记录左边和右边的最大高度。这样可以确保在计算积水量时,我们总是有一个参考的最大高度。

  3. 计算积水量:如果左边的高度小于右边的高度,那么我们移动左指针,并更新 left_max。此时,左指针位置的积水量为 left_max - height[left]。同理,如果右边的高度小于左边的高度,我们移动右指针,并更新 right_max,右指针位置的积水量为 right_max - height[right]

逻辑点 

  1. 讲道理这个解法我是想不出来的,把代码看懂就行了。我就模拟一下代码运行步骤,以题目给的图为例:

  • 初始状态:height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1];left = 0,right = 11;left_max = height[0] = 0,right_max = height[11] = 1;water_trapped = 0。

  • 第一步:比较 height[left] 和 height[right],即 height[0] 和 height[11],因为 0 < 1,所以移动左指针;left += 1,现在 left = 1;更新 left_max,left_max = max(0, height[1]) = max(0, 1) = 1;计算积水量,water_trapped += left_max - height[1] = 1 - 1 = 0;当前状态:left = 1,right = 11,left_max = 1,right_max = 1,water_trapped = 0。

  • 第二步:比较 height[left] 和 height[right],即 height[1] 和 height[11],因为 1 < 1 不成立,所以移动右指针;right -= 1,现在 right = 10;更新 right_max,right_max = max(1, height[10]) = max(1, 2) = 2;计算积水量,water_trapped += right_max - height[10] = 2 - 2 = 0;当前状态:left = 1,right = 10,left_max = 1,right_max = 2,water_trapped = 0。

  • 第三步:比较 height[left] 和 height[right],即 height[1] 和 height[10],因为 1 < 2,所以移动左指针;left += 1,现在 left = 2;更新 left_max,left_max = max(1, height[2]) = max(1, 0) = 1;计算积水量,water_trapped += left_max - height[2] = 1 - 0 = 1;当前状态:left = 2,right = 10,left_max = 1,right_max = 2,water_trapped = 1。

  • 第四步:比较 height[left] 和 height[right],即 height[2] 和 height[10],因为 0 < 2,所以移动左指针;left += 1,现在 left = 3;更新 left_max,left_max = max(1, height[3]) = max(1, 2) = 2;计算积水量,water_trapped += left_max - height[3] = 2 - 2 = 0;当前状态:left = 3,right = 10,left_max = 2,right_max = 2,water_trapped = 1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值