数组 | 二分法 | leecode做题笔记:搜索插入位置、x的平方根、搜索旋转排序数组、有效的完全平方数

总结二分法的题目与解法

适用题型:将数组分成两部分,要求解的答案一定在其中的一半里。左右两侧只要确定可以甩掉一边,就可以二分。
:二分法不一定要满足有序才能使用。只不过无序数组的写法复杂一点
二分法的时间复杂度:O(logn)【注:O读作big O】【底数为2】

题型1:在一个有序数组中,找某一个数是否存在
方法二分法,找到就停。
.
题型2:在一个有序数组中,找>=指定数的最左侧的位置
方法二分法,二分到结束才停。
.
题型3:局部最小值。数组中array无序,任何两个相邻的数不相等。局部最小:若n-1位置数比N-2位置的数小,则N-1位置就是局部最小。要求时间复杂度小于O(N)
方法:二分到底,一定可以找到一个局部最小值的。

优化流程的方向:

1、数据状况
2、问题标准

35.简单 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
.
请必须使用时间复杂度为 O(log n) 的算法。

解:所用语言为python3

引用:轻松理解 left + ((right -left) >> 1)

问题 :为什么不直接用(L+R) // 2 而用L + ((R-L)) >> 1)
答: 是因为
L+R在某种情况下可能会超过基本类型所能容纳的最大值而溢出,因此将(L+R)//2改写为:L+(R-L)//2
②又>> (位运算) 比 / 运算要快,>>1等价于//2所以:
L+(R-L)//2 = L+((R-L)>>1)

例:left = 0, right = 3
right-left = 3 = 011(二进制)
(right-left) >> 1 = 001
注:>>运算为整体向右移动,移出的作废,右侧补零
((right-left) >> 1)+ left = 1+0 = 1
作用等价于:(left + right)// 2 = 1

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)-1
        ans = len(nums)

        while left <= right:
            mid = ((right - left) >> 1) + left  # 等价于`mid = (left + right) // 2`
			
			# 判断语句的写法为:要判断的目标值<=给定的数组中间值
			# 因为当target不在数组中时,返回的位置索引是第一个>=它的值所在的位置所引
            if (target <= nums[mid]):  # 目的是找出第一个>=target的值           
                ans = mid
                right = mid - 1            
            else:
                left = mid + 1  # 如果target>nums[mid],说明要右移
        return ans

69.简单 x的平方根

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
.
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
.
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

解:所用语言为python3

class Solution:
    def mySqrt(self, x: int) -> int:
        left = 0  # 初始化左边界
        right = x  # 初始化右边界
        ans = -1  # 用于记录中间值的变量
        while left <= right:  # 当左边界 <= 右边界
            mid = ((right - left) >> 1) + left  # 中间值 = (左边界 + 右边界)// 2
            if (mid*mid <= x):  # 判断条件,根据题意得来:如果mid² <= x
                ans = mid  # 记录此时的中间值,作为下一次二分法的端点
                left = mid + 1  # 右移:左边界 = mid + 1
            else:
                right = mid - 1  # 否则左移:右边界 = mid - 1
        return ans  # 返回中间值

367. 简单有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false
进阶:不要 使用任何内置的库函数,如 sqrt

class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        left = 0 
        right = num  
        
        while left <= right:  
            mid = (left + right) // 2  

            if num >= (mid * mid): 
                if num == mid * mid:  
                    return True
                    
                left = mid + 1 
            else: 
                right = mid - 1

        return False

704. 简单二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

class Solution:
    def search(self, nums: List[int], target: int) -> int:

        left = 0
        right = len(nums) - 1
        ans = -1
        
        while left <= right:
            mid = (left + right) // 2  # 可以写为:((right - left)>>1) + left
            
            if target >= nums[mid]:
                ans = mid
                left = mid + 1
                
                if nums[ans]==target:
                    return ans

            else:
                r = mid - 1
                
        return -1

33. 中等 搜索旋转排序数组

数组 nums 按升序排列,数组中的值 互不相同 。
.
在传递给函数之前,nums 在预先未知的某个下标k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]
.
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1

题目分析:题目中的旋转旨在告诉我们,这个数组不是有序的,准确来讲是部分有序。
因此,这道题目比传统的有序数组处理复杂一点。

解1(不使用二分法):
所用语言为python3
从左往右遍历,时间复杂度是O(N)

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if target in nums:
        	return nums.index(target)
        else:
			return ans

解2(使用二分法):
所用语言为python3

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:  # 如果不存在【if target not in nums】
            return -1 # 直接返回-1
        left, right = 0, len(nums) - 1  #常规初始化
        while left <= right:            # 当左边界<=右边界时
            mid = ((right - left) >> 1) + left   # 中间值
            if nums[mid] == target:  # 直到找出中间值==目标值时
                return mid           # 返回中间值的索引
            if nums[0] <= nums[mid]: # 如果第一个元素<中间元素,说明[0,mid]这一半是有序的
                if nums[0] <= target < nums[mid]:  # 如果目标值在其中
                    right = mid - 1                    # 左移:右边界 - 1
                else:                              # 否则
                    left = mid + 1                    # 右移:左边界 + 1
            else:                                  # [mid,-1]是有序的
                if nums[mid] < target <= nums[len(nums) - 1]:  # 如果目标值在其中
                    left = mid + 1     # 右移:左边界 + 1
                else:               # 否则:
                    right = mid - 1     # 左移:右边界 - 1
        return -1  # 如果当左边界>右边界时,依旧没有找到目标值,则返回-1

34. 中等 在排序数组中查找元素的第一个和最后一个位置

在这里插入图片描述

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:

        # 查找左边界(刚好小于target的位置)
        left = 0
        right = len(nums) - 1
        leftIndex = -2
  
        while left <= right:
            mid = ((right - left) >>1) + left
            if (nums[mid] >= target):    
                right = mid - 1
                leftIndex = right
            else:
                left = mid + 1

        # 查找右边界(等于target的最后一个位置,即`大于target的第一个位置`-1)
        left = 0
        right = len(nums) - 1
        rightIndex = -2
  
        while left <= right:
            mid = ((right - left) >>1) + left
            if (nums[mid] > target):    
                right = mid - 1
            else:
                left = mid + 1
                rightIndex = left

        # print(f"leftIndex = {leftIndex}")
        # print(f"rightIndex = {rightIndex}")
        if (rightIndex == -2 or leftIndex == -2):  # [2,2] 3, return [-1, -1], rather than [-1, 1]
            return [-1, -1]
        elif (rightIndex - leftIndex)>1:
            return [leftIndex+1, rightIndex-1]
        return [-1, -1]
        

74. 中等 搜索二维矩阵

81. 中等 搜索旋转排序数组 II

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值