python实现LeetCode算法(1)之二分查找

仅作为个人算法训练笔记

1、数组中寻找目标数字

  • 题目1:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
  • 我的做法:时间复杂度应该为O(n) ,因为最差的结果就是循环n次
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if nums == []:  # 如果数组为空,返回-1
            return -1
        else:          # 数组不为空,对数组进行循环遍历
            for i in range(len(nums)):
                if nums[i] == target:   # 如果数组中存在目标数字,则返回目标数字的索引i, 如果数组中不存在目标数字,返回-1
                    return i
            return -1
  • 力扣官方二分查找定义说明及答案: (二分查找仅限于有序数组) 时间复杂度O(logn)
  • 在升序数组nums中寻找目标值target,对于特定下标 i ,比较nums[i]和target的大小:
    • 如果nums[i] = target,则下标 i 即为要寻找的下标;
    • 如果nums[i] > target,则 target 只可能在下标 i 的左侧;
    • 如果nums[i] < target,则 target 只可能在下标 i 的右侧。

二分查找的做法是,定义查找的范围[left,right],初始查找范围是整个数组。每次取查找范围的中点 mid,比较nums[mid] 和 target 的大小,如果相等则 mid 即为要寻找的下标;如果不相等则根据 nums[mid] 和 target 的大小关系将查找范围缩小一半。由于每次查找都会将查找范围缩小一半,因此二分查找的时间复杂度是O(logn),其中 n 是数组的长度。
二分查找的条件是查找范围不为空,即left≤right。如果 target 在数组中,二分查找可以保证找到 target,返回 target 在数组中的下标。如果target 不在数组中,则当left>right 时结束查找,返回-1。

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

2、第一个错误的版本

  • 题目2:你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
  • 思路:当一个版本为正确版本,则该版本之前的所有版本均为正确版本;当一个版本为错误版本,则该版本之后的所有版本均为错误版本。我们可以利用这个性质进行二分查找。
  • 具体地,将左右边界分别初始化为 1和 n,其中 n 是给定的版本数量。设定左右边界之后,每次我们都依据左右边界找到其中间的版本,检查其是否为正确版本。如果该版本为正确版本,那么第一个错误的版本必然位于该版本的右侧,我们缩紧左边界;否则第一个错误的版本必然位于该版本及该版本的左侧,我们缩紧右边界。这样我们每判断一次都可以缩紧一次边界,而每次缩紧时两边界距离将变为原来的一半,因此我们至多只需要缩紧 O(logn) 次。
class Solution:
    def firstBadVersion(self, n):
        left,right = 0,n  # 设置左右边界
        while left < right:  # 左边界<右边界时
            mid = left + (right-left)//2  # 取中间数 防止下标溢出
            if isBadVersion(mid) is True:  # 当输入版本号是错误版本时,第一个错误版本应该在左边
                right = mid       # 将区间缩到[left,mid]  不减1是因为mid可能就是第一个错误版本
            else:          # 版本号不是错误版本时,左边的版本都是正确版本,第一个错误版本出现在右侧
                left = mid + 1    # 将区间缩到[mid+1,right] 加1是因为输入版本号已经是正确版本了,因此后续研究可以舍掉了
        return left  # 当左边界=右边界时就剩下一个点了,此时该点即为第一个错误版本

3、搜索插入位置

  • 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。
  • 和题目1唯一不同的是,若不存在则按顺序插入输出所在下标
class Solution:
    def searchInsert(self, nums, target):
        left,right= 0, len(nums)-1
        while left <=right:
            mid = left+(right-left)//2
            if nums[mid]<target:
                left = mid+1
            elif nums[mid]>target:
                right=mid-1
            else:
                return mid
        return right+1 # 若left>right 说明不存在目标数字,同时在nums[right+1]处插入目标数字

你可能感兴趣的:(Leetcode算法题,python,数据结构,算法)