二分查找 数组峰值 旋转排序数组 - 灵神视频总结

二分查找 红蓝染色法 - 灵神视频总结-CSDN博客

在听这节课之前,需要先听下二分查找 的基础知识。

我基本明白了二分查找的套路。

尤其是前一篇文章要好好啃下来,弄明白 红蓝染色法 是怎么弄的。

  • 1 <= nums.length <= 1000
  • -231 <= nums[i] <= 231 - 1
  • 对于所有有效的 i 都有 nums[i] != nums[i + 1]

搞不明白 搞不明白 不会做 !!!

我要搞定它,弄明白 弄明白 !

这题我看了好多题解,实在不懂。后来又个小伙子的评论我看了就懂了。

也许很多人看了,还是不懂,图解也只是迷迷糊糊根据答案来,我这里通俗解释以下,这道题,最最最重要的是条件,条件,条件,两边都是负无穷,数组当中可能有很多波峰,也可能只有一个,如果尝试画图,就跟股票信息样,没有规律,如果根据中点值判断我们的二分方向该往何处取,这道题还有只是返回一个波峰。你这样想,中点所在地方,可能是某座山的山峰,山的下坡处,山的上坡处,如果是山峰,最后会二分终止也会找到,关键是我们的二分方向,并不知道山峰在我们左边还是右边,送你两个字你就明白了,爬山(没错,就是带你去爬山),如果你往下坡方向走,也许可能遇到新的山峰,但是也许是一个一直下降的坡,最后到边界。但是如果你往上坡方向走,就算最后一直上的边界,由于最边界是负无穷,所以就一定能找到山峰,总的一句话,往递增的方向上,二分,一定能找到,往递减的方向只是可能找到,也许没有。

山峰的概念,左边的元素比它小,右边的元素也比它小。

题目中有个含义 可以假设 nums[-1] = nums[n] = -inf, 那么最后一个元素一定是下坡状态。

我们按照上坡的状态才能找到山峰的位置, 所以可以把比较公式 nums[m] < nums[m+1], 满足条件的为true, 涂蓝色,R = m-1, 不满足条件,设置为false,下坡状态, 设置为红色。不断循环遍历。 找到第一个满足条件的边界,就是我们要求的解。

解法

1️⃣ 暴力解法: 一次遍历 ,判断前后两个元素是否比当前元素小,如果是,返回下标。 时间复杂度 O(n), 不满足题意要求,需要设计O(log(n) 时间复杂度。

2️⃣ 二分查找, 红蓝染色法。

设置两个指针 L R ,分别指向 L = 0 , R = n -2 ,使用左闭右闭的区间。

( 这里说明下为什么 R= n-2 ,因为 nums[n] = -inf, nums[n-1] 一定是下坡的,它可能是个峰值元素,如果整个数组是单调递增的。)

M = (L + R)//2 , 比较 nums[M] 和 nums[M+1] ,

如果nums[M] < nums[M+1] ,判断的是M这个元素,这个元素 比上个元素小,那它是上坡状态, 它一定不是峰值元素, 让M 左边的元素 都涂红色,L = M +1 ,根据区间不变量, M-1 也都涂红色。

如果 nums[M] > nums[M+1] , 判断是M这个元素涂什么颜色, 这个元素比下个元素大,它很可能是峰值,也可能是下坡的半山腰, 这个元素涂蓝色,继续往中间部分去找。 M +1 都涂蓝色。

如此循环迭代。

如果还是不懂,可以手动去涂色,多找几个例子,不停的画画,就能找到感觉了。

看代码:

class Solution(object):
    def findPeakElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        left = 0 
        right = len(nums) -2 
        while left <= right:
            mid = (left + right)//2
            if nums[mid] < nums[mid+1]: # 上坡状态,需要往后找。
                left = mid +1
            else: # 下坡或峰值,需要往前找。
                right = mid - 1
        return left
        
so = Solution()
nums = [1,2,3,1] #[1,2,1,3,5,6,4]
print(so.findPeakElement(nums))

153. 寻找旋转排序数组中的最小值

题解

1️⃣ 暴力解法: 一次遍历,设置一个变量min,遍历所有的数字,和min对比,如果比min小,更新。 时间复杂度为O(n) , 空间复杂度为O(1), 不满足题意要求。

2️⃣ 二分查找。

设置 两个指针,L R 使用闭区间, L = 0, R = len(nums) - 2 , 和最后一个元素对比,

mid = (L +R ) // 2

  1. 如果nums[mid] < nums[-1] ,移动right = mid - 1, 也就是最小元素不在 [mid, right] 中,在[left, mid] 中。
  2. 如果nums[mid] < nums[-1] , 移动left = mid + 1, 也就是最小元素不在 [left, mid] 中,在[mid, right] 中。

如果想不清楚,找个例子画一画就好了.

代码

class Solution(object):
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        left = 0 
        right = len(nums) - 2
        while left <= right:
            mid = (left + right) // 2 
            if nums[mid] > nums[-1]:
                left = mid + 1 
            else:
                right = mid - 1
        return nums[left]



nums = [4,5,6,7,0,1,2]
so = Solution()
print(so.findMin(nums))

33. 搜索旋转排序数组

解法:

二分法。
这题是要找到target的索引。

  1. 先找到最小元素;
  2. 在正确的部分,二分查找,找到其中的索引。

两次二分查找即可,思路清晰明了。

直接看代码吧:

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        i = self.findMin(nums)
        if target > nums[-1]:
            left , right = 0, i-1
        else:
            left , right = i, len(nums)-1
        return self.lower_bound(nums, left, right, target)

    def findMin(self,nums):
        """ 获取最小元素
        """
        left = 0 
        right = len(nums) - 2
        while left <= right:
            mid = (left + right)//2
            if nums[mid] > nums[-1]:
                left = mid + 1 
            else:
                right = mid - 1  
        return left 
    
    def lower_bound(self, nums, left, right, target):
        """
        在 left ... right 这个数组中找  
        """
        while left <= right : 
            mid = (left + right)//2
            if nums[mid] < target : # 
                left = mid + 1 
            else:
                right = mid - 1 
        return left if nums[left] == target else -1 
        


nums = [4,5,6,7,0,1,2]
target = 0
so = Solution()
print(so.search(nums, target))

x 的平方根

69. x 的平方根 这题也经常考。

这里写一写,如果写不对,debug一下。这里面有个难点。

class Solution(object):
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        left = 1 
        right = x 
        while left <= right: # 左闭右闭区间
            mid = (left + right) //2 
            if mid * mid == x:
                return mid 
            elif mid * mid < x: # 左侧涂红色,
                left = mid + 1 
            else: # 涂蓝色
                right = mid - 1 
        
        return left-1 # 可以手动遍历一下就知道。
    
so = Solution()
print(so.mySqrt(24))

  • 32
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值