深入理解二分查找算法(一)

简介: 深入理解二分查找算法(一)

🍋引言

二分查找是一种高效的搜索算法,特别适用于有序数组。它通过将待查找区间逐渐缩小一半的方式,快速定位目标元素。在本文中,我们将深入探讨二分查找算法的原理、应用场景以及实现方式。

🍋基本原理

二分查找的基本原理是不断缩小待查找区间,通过比较中间元素与目标值的大小来确定下一步搜索的方向。这种分而治之的思想使得算法的时间复杂度保持在 O(log n) 的水平,是一种非常高效的搜索算法。

🍋算法步骤

初始化左右边界:设定初始的搜索区间,通常为整个数组。
计算中间索引:通过计算左右边界的中点来确定中间索引。
比较中间元素:将中间索引对应的元素与目标值进行比较。
调整搜索范围:根据比较结果,调整左右边界,缩小搜索范围。
重复执行:重复以上步骤,直到找到目标元素或搜索范围为空。

🍋应用场景

有序数组搜索: 二分查找最常见的应用场景是在有序数组中查找元素。
搜索区间问题: 可以用于解决搜索区间的问题,如在旋转有序数组中查找最小值。
近似查找: 用于查找最接近目标值的元素。

🍋例题

有的题并没有使用二分查找

🍋1608. 特殊数组的特征值

给你一个非负整数数组 nums 。如果存在一个数 x ,使得 nums 中恰好有 x 个元素 大于或者等于 x ,那么就称 nums 是一个 特殊数组 ,而 x 是该数组的 特征值 。

注意: x 不必 是 nums 的中的元素。

如果数组 nums 是一个 特殊数组 ,请返回它的特征值 x 。否则,返回 -1 。可以证明的是,如果 nums 是特殊数组,那么其特征值 x 是 唯一的 。

class Solution(object):
    def specialArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if all(element == 0 for element in nums) == True:
            return -1
        count1 = 0
        for i in range(1, len(nums) + 1): # 1,2
            for j in range(len(nums)):
                if nums[j] >= i:
                    count1 += 1
            if count1 != i:
                count1 = 0
                continue
            else:
                return i
        return -1

🍋2389. 和有限的最长子序列

给你一个长度为 n 的整数数组 nums ,和一个长度为 m 的整数数组 queries 。

返回一个长度为 m 的数组 answer ,其中 answer[i] 是 nums 中 元素之和小于等于 queries[i] 的 子序列 的 最大 长度 。

子序列 是由一个数组删除某些元素(也可以不删除)但不改变剩余元素顺序得到的一个数组。

from itertools import accumulate
from bisect import bisect_left, bisect_right
class Solution:
    def answerQueries(self, nums: List[int], queries: List[int]) -> List[int]:
        nums.sort()
        s = list(accumulate(nums))
        return [bisect_right(s, q) for q in queries]
    # def answerQueries(self, nums: List[int], queries: List[int]) -> List[int]:
    #     nums.sort()
    #     m = len(queries)
    #     ans = [0] * m
    #     idx = sorted(range(m), key=lambda i: queries[i])
    #     s = j = 0
    #     for i in idx:
    #         while j < len(nums) and s + nums[j] <= queries[i]:
    #             s += nums[j]
    #             j += 1
    #         ans[i] = j
    #     return ans

🍋744. 寻找比目标字母大的最小字母

给你一个字符数组 letters,该数组按非递减顺序排序,以及一个字符 target。letters 里至少有两个不同的字符。

返回 letters 中大于 target 的最小的字符。如果不存在这样的字符,则返回 letters 的第一个字符。

from bisect import bisect_right
class Solution:
    def nextGreatestLetter(self, letters: List[str], target: str) -> str:
        position_right = bisect_right(letters, target)
        if position_right == len(letters):
            return letters[0]
        return letters[position_right]

🍋704. 二分查找

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

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        low = 0
        high = len(nums)-1
        while low<=high:
            mid = low + (high-low)//2
            if nums[mid] == target:
                return mid
            elif nums[mid]>target:
                high = mid - 1
            else:
                low = mid + 1
        return -1

🍋888. 公平的糖果交换

爱丽丝和鲍勃拥有不同总数量的糖果。给你两个数组 aliceSizes 和 bobSizes ,aliceSizes[i] 是爱丽丝拥有的第 i 盒糖果中的糖果数量,bobSizes[j] 是鲍勃拥有的第 j 盒糖果中的糖果数量。

两人想要互相交换一盒糖果,这样在交换之后,他们就可以拥有相同总数量的糖果。一个人拥有的糖果总数量是他们每盒糖果数量的总和。

返回一个整数数组 answer,其中 answer[0] 是爱丽丝必须交换的糖果盒中的糖果的数目,answer[1] 是鲍勃必须交换的糖果盒中的糖果的数目。如果存在多个答案,你可以返回其中 任何一个 。题目测试用例保证存在与输入对应的答案。

class Solution:
    def swap(self,a, b):
        return b, a 
    # def fairCandySwap(self, aliceSizes: List[int], bobSizes: List[int]) -> List[int]:  # 超时
    #     result1 = 0
    #     result2 = 0
    #     l_ = []
    #     for i in range(len(aliceSizes)):
    #         for j in range(len(bobSizes)):
    #             aliceSizes[i],bobSizes[j] = self.swap(aliceSizes[i],bobSizes[j])
    #             if sum(bobSizes) == sum(aliceSizes):
    #                 l_.append(bobSizes[j])
    #                 l_.append(aliceSizes[i])
    #                 return l_
    #             aliceSizes[i], bobSizes[j] = self.swap(aliceSizes[i], bobSizes[j])
    def fairCandySwap(self, aliceSizes: List[int], bobSizes: List[int]) -> List[int]:
        # sum(aliceSizes)-A+B=sum(bobSizes)-B+A
        # A-B=tmp=(sum(aliceSizes)-sum(bobSizes))//2
        tmp = (sum(aliceSizes) - sum(bobSizes)) >> 1
        for a in aliceSizes:
            if a - tmp in bobSizes: return [a, a - tmp]

🍋268. 丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        # for i in range(len(nums)+1):
        #     if i not in nums:
        #         return i   # 过了,但是太慢
        nums.sort()
        for i, num in enumerate(nums):
            if num != i:
                return i
        return len(nums)

🍋278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:
class Solution:
    def firstBadVersion(self, n: int) -> int:
        low = 0
        high = n-1
        while low<=high:
            mid = low + (high-low)//2
            if isBadVersion(mid)==True :
                high = mid - 1
            else:
                low = mid + 1
        return low

挑战与创造都是很痛苦的,但是很充实。

相关文章
|
3月前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
1月前
|
算法 C# 索引
C#二分查找算法
C#二分查找算法
|
1月前
|
存储 算法 C语言
【C语言】二分查找算法
【C语言】二分查找算法
|
1月前
|
消息中间件 存储 算法
一文搞懂二分查找算法!
一文搞懂二分查找算法!
|
1月前
|
算法 Java 索引
数据结构与算法学习十五:常用查找算法介绍,线性排序、二分查找(折半查找)算法、差值查找算法、斐波那契(黄金分割法)查找算法
四种常用的查找算法:顺序查找、二分查找(折半查找)、插值查找和斐波那契查找,并提供了Java语言的实现代码和测试结果。
18 0
|
3月前
|
存储 算法 Java
深入算法基础二分查找数组
文章深入学习了二分查找算法的基础,通过实战例子详细解释了算法的逻辑流程,强调了确定合法搜索边界的重要性,并提供了Java语言的代码实现。
深入算法基础二分查找数组
|
3月前
|
算法
【算法】二分查找——二分查找
【算法】二分查找——二分查找
|
4月前
|
算法
【算法】二分查找(整数二分和浮点数二分)
算法学习——二分查找(整数二分和浮点数二分)
41 0
【算法】二分查找(整数二分和浮点数二分)
|
5月前
|
存储 算法 C语言
二分查找算法的概念、原理、效率以及使用C语言循环和数组的简单实现
二分查找算法的概念、原理、效率以及使用C语言循环和数组的简单实现
|
5月前
|
机器学习/深度学习 算法 索引
数据结构算法--1 顺序查找二分查找
**顺序查找时间复杂度为O(n)**,适合无序列表,可以通过`enumerate`或直接遍历索引来实现。**二分查找时间复杂度为O(logn)**,适用于有序列表,利用Python中`left`、`right`指针和`mid`点不断缩小搜索范围。效率上二分查找更优。