【基础算法精讲 05】二分查找

5 阅读3分钟

题目:162. 寻找峰值 - 力扣(LeetCode)

题解: 双指针 二分查找获取目标索引。

空间复杂度:O(1)

时间复杂度:O(logn),n 为 数组nums的长度.

代码实现:

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        # 注意左右边界
        left, right = -1, len(nums) - 1 # 避免边界溢出
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] > nums[mid + 1]:
                right = mid
            else:
                left = mid
        return right

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

题解: 双指针,二分查找思路。

空间复杂度:O(1)

时间复杂度:O(logn), n 为nums数组的长度.

代码实现:

class Solution:
    def findMin(self, nums: List[int]) -> int:
        left, right = -1, len(nums) - 1
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] > nums[-1]: # mid索引值和最后一个值比较 来确认区间缩减方向
                left = mid
            else:
                right = mid
        return nums[right]

题目:33. 搜索旋转排序数组 - 力扣(LeetCode)

题解:

已知:原数组,升序排序且无重复元素,分两步解题。

step1: 先获取到数组最小值对应的索引,用来划分区间。条件用:nums[mid]nums[-1]比较俩缩小区间

step2: 原区间有序,被分割后的两个区间也是有序的,根据目标区间进行二分查找即可。

复杂度:

空间复杂度:O(1)

时间复杂度:O(logn * logn)

代码实现:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        index_0 = self.min_index(nums, -1, len(nums) - 1)
        if target > nums[-1]:
            i = self.get_target(nums, -1, index_0, target)
        else:
            i = self.get_target(nums, index_0 - 1, len(nums), target)
        if nums[i] == target:
            return i
        else:
            return -1

    # step1: 获取最小值对应的索引下标
    def min_index(self, nums: List[int], left: int, right: int) -> int:
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] > nums[-1]:
                left = mid
            else:
                right = mid
        return right

    # step2: 根据区间获取目标值
    def get_target(self, nums: List[int], left: int, right: int, target: int) -> int:
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] >= target:
                right = mid
            else:
                left = mid
        return right

题目:74. 搜索二维矩阵

题解:

遍历每行,通过判断 target 是在区间[matrix[m][0], matrix[m][-1]],如果在就进入该行进行二分查找,如果目标值为 target 直接返回 True ,否则持续遍历结束,最后 right 指针对应着潜在的目标值 target ,如果 nums[right] == target 则返回 True ,否则返回 False.

复杂度:

空间复杂度:O(1)

时间复杂度:O(m * logn), m 代表矩阵的行,n代表矩阵的列.

代码实现:

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        
        for mat in matrix:
            if target >= mat[0] and target <= mat[len(mat) - 1]:
                left, right = -1, len(mat)
                while left + 1 < right:
                    mid = (left + right) // 2
                    if mat[mid] > target:
                        right = mid
                    elif mat[mid] < target:
                        left = mid
                    else:
                        return True
            else:
                continue
            if mat[right] == target:
                return True
            else:
                return False

题目:1901. 寻找峰值 II

题解:

见题解:1901. 寻找峰值 II - 力扣(LeetCode)

复杂度:

空间复杂度:O(1)

时间复杂度:O(logm), m 表示矩阵的行数.

代码实现:

class Solution:
    def findPeakGrid(self, mat: List[List[int]]) -> List[int]:
        left, right = -1, len(mat) - 1
        while left + 1 < right:
            i = (left + right) // 2
            mx = max(mat[i])
            if mx > mat[i + 1][mat[i].index(mx)]:
                right = i
            else:
                left = i
        return [right, mat[right].index(max(mat[right]))]

题目:154. 寻找旋转排序数组中的最小值 II

题解:

复杂度:

空间复杂度:O(1)

时间复杂度:O(logn)

代码实现:

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