力扣刷题笔记 二分查找以及相关题目(含ACM模式)

本文详细介绍了二分查找法的基本原理、循环不变量的定义、左闭右闭和左闭右开两种模式的代码实现,以及在ACM竞赛中的应用场景,包括搜索插入位置、查找元素范围和判断完全平方数等题目

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《代码随想录》学习笔记,原链接:https://programmercarl.com/

704.二分查找

(1)二分查找法前提

(2)循环不变量(区间定义)

(3)二分查找法代码

(4)ACM模式

二分查找相关题目

(1)35.搜索插入位置

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

(3)69.x的平方根

(4)367.有效的完全平方数


704.二分查找

力扣题目链接

(1)二分查找法前提

  • 数组为有序数组
  • 数组中无重复元素(不然可能返回的下标不唯一)

(2)循环不变量(区间定义)

        区间的定义就是不变量,循环中坚持根据区间的定义来做边界处理,就是循环不变量规则。

        一般的区间定义分为两种:

  • 左闭右闭 [left, right],即能取到left和right两个端点:
left, right = 0, len(nums) - 1    # 定义左闭右闭的区间端点,[left, right]

while left <= right:
    middle = left + (right - left) // 2

    if target < nums[middle]:
        right = middle - 1    # target在区间的左半部分,即在[left, middle - 1]范围内
    elif target > nums[middle]:
        left = middle + 1    # target在区间的右半部分,即在[middle + 1, right]范围内

  • 左闭右开 [left, right),即只能取到左端点left而不能取到右端点right:
left, right = 0, len(nums) - 1    # 定义左闭右闭的区间端点,[left, right]

while left < right:
    middle = left + (right - left) // 2

    if target < nums[middle]:
        right = middle    # target在区间的左半部分,即在[left, middle]范围内
    elif target > nums[middle]:
        left = middle + 1    # target在区间的右半部分,即在[middle + 1, right]范围内

(3)二分查找法代码

        时间复杂度 O(log n),空间复杂度O(1)。

  • 左闭右闭:
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1    # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            if nums[middle] > target:
                right = middle - 1    # target在区间的左半部分,即在[left, middle - 1]范围内
            elif nums[middle] < target:
                left = middle + 1    # target在区间的右半部分,即在[middle + 1, right]范围内
            else:
                return middle    # 数组中找到target值,直接返回对应的下标

        return -1    # 未找到target值

  • 左闭右开:
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums)    # 定义左闭右开的区间端点,[left, right)

        while left < right:    # 因为不能取到右端点right,所以left == right的时候,在[left, right)里表示无效的空间,所以使用 <
            middle = left + (right - left) // 2

            if nums[middle] > target:
                right = middle    # target在区间的左半部分,即在[left, middle)范围内
            elif nums[middle] < target:
                left = middle + 1    # target在区间的左半部分,即在[middle + 1, right)范围内
            else:
                return middle    # 数组中找到target值,直接返回对应的下标

        return -1    # 未找到target值

【注】“search(self, nums: List[int], target: int) -> int:”中的List[int]和int表示类型提示。但是Python 程序在运行时不强制执行函数和变量的类型提示,也就是说写错了也不会影响程序运行,但是不建议这么做。

(4)ACM模式

class Solution:
    def search(self, nums, target):
        left, right = 0, len(nums) - 1    # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            if nums[middle] > target:
                right = middle - 1    # target在区间的左半部分,即在[left, middle - 1]范围内
            elif nums[middle] < target:
                left = middle + 1    # target在区间的右半部分,即在[middle + 1, right]范围内
            else:
                return middle    # 数组中找到target值,直接返回对应的下标

        return -1    # 未找到target值

# 自定义数据列表和target值
nums = list(map(int, input("输入已排序的整数数组:").split(",")))
target = int(input("输入target值:"))

# 二分查找
binarySearch = Solution()
result = binarySearch.search(nums, target)
print(result)

【注1】list(map(int, input("输入已排序的整数数组:").split(","))),分析:

  • input() 函数:用于从用户输入获取一个由逗号分隔的整数组成的字符串。
  • split() 方法:用于将输入的字符串按照逗号分隔成多个子字符串,并返回子字符串构成的列表。如输入1,2,3时,将返回['1', '2', '3']。
  • map() 函数:用于对子字符串列表中的每个元素应用指定的函数。具体来说,map() 函数接收两个参数,第一个参数是一个函数,第二个参数是一个可迭代对象,它将该函数依次作用于可迭代对象中的每个元素,并返回一个由结果组成的新的可迭代对象。例如map(int, ['1', '2', '3']) ,会将字符串列表中的每个元素转换为整数类型,并返回一个由整数组成的新的可迭代对象 [1][2][3]。
  • int() 函数:用于将字符串转换为整数类型的函数。
  • list() 函数:用于将可迭代对象 [1][2][3]转换为列表类型[1, 2, 3],从而得到用户输入的整数列表。

【注2】可迭代对象:

简单来说,任何可以循环遍历的对象都是可迭代对象,如字符串、列表、元组、集合、字典等等之类的对象都属于可迭代对象。

  • 使用isinstance()函数,可以判断对象是否为可迭代对象

  • 使用 dir()函数 查看对象内所有的属性与方法,如dir({"name": "ayaka"})。之后发现,可迭代对象都构建了 ``__iter__`` 方法,而不可迭代对象没有构建,因此也可通过此特点来判断某一对象是不是可迭代对象。

二分查找相关题目

(1)35.搜索插入位置

力扣题目链接

        首先,这道题目的前提是数组是有序数组,是使用二分查找的基础条件。

        同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的。

  • 核心代码模式
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1    # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            if target < nums[middle]:
                right = middle - 1    # target在区间的左半部分,即在[left, middle - 1]范围内
            elif target > nums[middle]:
                left = middle + 1    # target在区间的左半部分,即在[middle + 1, right)范围内
            elif target == nums[middle]:
                return middle    # 返回目标值在数组中的索引

        return left    # 返回被插入的位置索引

【注】关于当目标值不存在与数组中时,最后返回的插入位置return left的推导分析:

  • ACM模式
class Solution:
    def searchInsert(self, nums, target):
        left, right = 0, len(nums) - 1    # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            if target < nums[middle]:
                right = middle - 1    # target在区间的左半部分,即在[left, middle - 1]范围内
            elif target > nums[middle]:
                left = middle + 1    # target在区间的左半部分,即在[middle + 1, right)范围内
            elif target == nums[middle]:
                return middle

        return left

# 自定义数据列表和target值
nums = list(map(int, input("输入已排序的整数数组:").split(",")))
target = int(input("输入target值:"))

# 搜索插入位置
Solution = Solution()
result = Solution.searchInsert(nums, target)
print(result)

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

力扣题目链接

  • 核心代码模式
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        left, right = 0, len(nums) - 1  # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            if target < nums[middle]:   # target在区间的左半部分,即在[left, middle - 1]范围内
                right = middle - 1
            elif target > nums[middle]: # target在区间的左半部分,即在[middle + 1, right)范围内
                left = middle + 1

            elif target == nums[middle]:
                mid_left, mid_right = middle, middle

                while mid_left >= left and nums[mid_left] == target:    # 寻找第一个位置
                    mid_left -= 1
                while mid_right <= right and nums[mid_right] == target: # 寻找第二个位置
                    mid_right += 1
                return [mid_left + 1, mid_right - 1]    # 返回元素的第一个位置和最后一个位置

        return [-1, -1]    # 未找到元素

【注】返回元素的第一个位置和最后一个位置分析:

  • ACM模式
class Solution:
    def searchRange(self, nums, target):
        left, right = 0, len(nums) - 1  # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            if target < nums[middle]:   # target在区间的左半部分,即在[left, middle - 1]范围内
                right = middle - 1
            elif target > nums[middle]: # target在区间的左半部分,即在[middle + 1, right)范围内
                left = middle + 1

            elif target == nums[middle]:
                mid_left, mid_right = middle, middle

                while mid_left >= left and nums[mid_left] == target:    # 寻找第一个位置
                    mid_left -= 1
                while mid_right <= right and nums[mid_right] == target: # 寻找第二个位置
                    mid_right += 1
                return [mid_left + 1, mid_right - 1]    # 返回元素的第一个位置和最后一个位置

        return [-1, -1]    # 未找到元素

# 自定义数据列表和target值
nums = list(map(int, input("输入已排序的整数数组:").split(",")))
target = int(input("输入target值:"))

# 在排序数组中查找元素的第一个和最后一个位置
Solution = Solution()
result = Solution.searchRange(nums, target)
print(result)

(3)69.x的平方根

力扣题目链接

  • 核心代码模式
class Solution:
    def mySqrt(self, x: int) -> int:
        left, right = 0, x     # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            # middle * middle 花费的时间远小于middle ** 2
            if middle * middle > x:     # 平方根在区间的左半部分,即在[left, middle - 1]范围内
                right = middle - 1
            elif middle * middle < x:    # 平方根在区间的右半部分,即在[middle + 1, right]范围内
                left = middle + 1
            elif middle * middle == x:
                return middle    # 返回x的平方根
        
        return right    # 返回x平方根的整数部分

【注】返回x平方根的整数部分为right的分析:

  • ACM模式
class Solution:
    def mySqrt(self, x):
        left, right = 0, x  # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            # middle * middle 花费的时间远小于middle ** 2
            if middle * middle > x:  # 平方根在区间的左半部分,即在[left, middle - 1]范围内
                right = middle - 1
            elif middle * middle < x:  # 平方根在区间的右半部分,即在[middle + 1, right]范围内
                left = middle + 1
            elif middle * middle == x:
                return middle  # 返回x的平方根

        return right  # 返回x平方根的整数部分

# 自定义数据x值
x = int(input("输入正整数值x:"))

# 在排序数组中查找元素的第一个和最后一个位置
Solution = Solution()
result = Solution.mySqrt(x)
print(result)

(4)367.有效的完全平方数

力扣题目链接

  • 核心代码模式
class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        left, right = 0, num    # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2 

            if middle * middle > num:   # 平方根在区间的左半部分,即在[left, middle - 1]范围内
                right = middle - 1
            elif middle * middle < num: # 平方根在区间的右半部分,即在[middle + 1, right]范围内
                left = middle + 1
            elif middle * middle == num:    
                return True
                
        return False    # 不是完全平方数
  • ACM模式
class Solution:
    def isPerfectSquare(self, num):
        left, right = 0, num  # 定义左闭右闭的区间端点,[left, right]

        while left <= right:
            middle = left + (right - left) // 2

            if middle * middle > num:  # 平方根在区间的左半部分,即在[left, middle - 1]范围内
                right = middle - 1
            elif middle * middle < num:  # 平方根在区间的右半部分,即在[middle + 1, right]范围内
                left = middle + 1
            elif middle * middle == num:
                return True

        return False  # 不是完全平方数

# 自定义数据num值
num = int(input("输入正整数值num:"))

# 有效的完全平方数
Solution = Solution()
result = Solution.isPerfectSquare(num)
print(result)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值