算法学习笔记1:二分查找

1. 二分查找

  • 排序的,或者间接排序的
  • 注意边界条件,因为每次循环只触发一个条件,所以 i i i或者 j j j只能移动一步,循环条件是 i ≤ j i\le j ij
训练计划

题目:704. 二分查找

代码模板:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        i, j = 0, len(nums)-1
        while i <= j:
            # 取靠左的,即 1 2 3 4 取2,1 2 3 取2
            # 防止计算时溢出
            mid = i + ((j-i) >> 1)
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                i = mid+1
            else:
                j = mid-1
        return -1

题目:278. 第一个错误的版本

代码模板:

# The isBadVersion API is already defined for you.
# @param version, an integer
# @return an integer
# def isBadVersion(version):

class Solution:
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        # 特殊之处在于只有2种状态,即当跳出循环时:
        # 如果是i未出错,i += 1,返回i;如果j未出错,一定是上一个版本出错,也返回i
        i, j = 1, n
        while i <= j:
            mid = i + ((j-i) >> 1)
            if isBadVersion(mid):
                j = mid-1
            else:
                i = mid+1
        return i

这道题也可以使用 i < j i<j i<j的条件判断了,并且每次只有一个指针移动。

为什么会有这样的区别呢?事实上是因为二分的区间条件不同,判断数字是3种情况,最终范围长度大于0时即是有效范围;但是判断正误只有2种情况,只有范围长度为1时,才是最终的答案,这里需要好好思考,将问题泛化到各种情况下。

题目:35. 搜索插入位置

代码模板:

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        # 3种情况,对比普通二分将返回-1变为返回合适的索引
        # 考虑找不到的情况,此时i=j,并且有一个变化使while条件不成立,大了j减1,小了i+1,所以返回i
        # 插入位置i表示插入到i索引之前
        i, j = 0, len(nums)-1
        while i <= j:
            mid = i + ((j-i) >> 1)
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                i = mid+1
            else:
                j = mid-1
        return i
相似题目

题目:34. 在排序数组中查找元素的第一个和最后一个位置(重点)

代码模板:

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # i和j就是区间2端点
        # 3种情况
        i, j = 0, len(nums)-1
        while i <= j:
            mid = i + ((j-i) >> 1)
            if nums[mid] < target:
                i = mid + 1
            elif nums[mid] > target:
                j = mid - 1
            # mid到j都等于target
            elif nums[i] != target:
                i += 1
            # i到mid都等于target
            elif nums[j] != target:
                j -= 1
            else:
                return [i, j]
        return [-1, -1]

这道题就比较有趣了,我的实现并不是完全的二分查找,但是在一定程度上使用二分查找优化了整体的效率,有时间可以学习一下官方的题解

题目:374. 猜数字大小

代码模板:

# The guess API is already defined for you.
# @param num, your guess
# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
# def guess(num: int) -> int:

class Solution:
    def guessNumber(self, n: int) -> int:
        i, j = 1, n
        while i <= j:
            mid = i + ((j-i) >> 1)
            if guess(mid) == 0:
                return mid
            elif guess(mid) == -1:
                j = mid-1
            else:
                i = mid+1
        # 返回0表示pick不在范围内
        return 0

题目:658. 找到 K 个最接近的元素(考察coding能力)

代码模板:

class Solution:
    def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]:
        # 二分找到恰好大于x一点点的位置,等价于之前的查找插入位置
        i, j = 0, len(arr)-1
        idx = -100
        while i <= j:
            mid = i + ((j-i) >> 1)
            if arr[mid] == x:
                idx = mid
                break
            elif arr[mid] < x:
                i = mid+1
            else:
                j = mid-1
        if idx == -100:
            idx = i
        # print(idx)
        # 缩小区间
        if idx == 0:
            return arr[:k]
        elif idx == len(arr):
            return arr[-k:]
        else:
            i, j = max(idx-k, 0), min(idx+k, len(arr)-1)
            while j-i >= k:
                if arr[j] - x > x - arr[i]:
                    j -= 1
                elif arr[j] - x < x - arr[i]:
                    i += 1
                else:
                    j -= 1
            return arr[i:j+1]

猜你喜欢

转载自blog.csdn.net/qq_45510888/article/details/123610830