Python算法-二分法(Binary Search)

二分法类似于双指针,不过二分的方法主要用于排序数组中元素的查找。

704. 二分查找

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

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

  • 二分查找法
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:
                left = mid + 1
            # 目标值在左侧
            elif nums[mid] > target:
                right = mid - 1
            # 恰好是目标值
            else:
                return mid
        return -1
  • 方法2:二分查找法的第二种形式
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:
                left = mid + 1
            # 排除右侧区间
            else:
                right = mid
        # 直接剩下left=right单一区间,判断是否相等
        if nums[left] == target:
            return left
        else:
            return-1
35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

输入: nums = [1,3,5,6], target = 5
输出: 2

class Solution:
    def searchInsert(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
            else:
                right = mid - 1
        # 只能返回左侧边界的数
        return left
69. Sqrt(x)

给你一个非负整数 x ,计算并返回 x 的 算术平方根

输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

class Solution:
    def mySqrt(self, x: int) -> int:
        left = 0
        right = x
        result = -1
        while left <= right:
            # 二分法为了减少时间复杂度
            mid = left + (right - left)//2
            # 因为结果只保留整数部分,因此求解目标是:找到 k^2 <= x 的最大结果
            if mid*mid <= x:
                result = mid
                left = mid + 1
            else:
                right = mid - 1
        return result
50. Pow(x, n)

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,x^n)。

  • 方法1:使用内置函数
class Solution:
    def myPow(self, x: float, n: int) -> float:
        return pow(x,n)
  • 方法2:递归+分治
    x→x^2 →x^4 →x^8 →x^16 →x^32 →x^64
    x→x^2 →x^4 →x^9 →x^19 →x^38 →x^77
    从右向左分析,可以得知递归的思路
class Solution:
    def myPow(self, x: float, n: int) -> float:
        def quickMul(N):
            # 二分的思路
            if N == 0:
                return 1.0
            y = quickMul(N // 2)
            # 判断是否除尽
            if N % 2 == 0:
                return y*y
            else:
                return y*y*x
        # 对正负进行分类判断
        if n >= 0:
            return quickMul(n)
        else:
            return 1.0 / quickMul(-n)
287. 寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。

输入:nums = [1,3,4,2,2]
输出:2

  • 使用二分法在排序数组中进行查找多余的数字
class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        left = 0
        right = len(nums) - 1    # 因为重复了一个数字,所以右区间少一个元素
        while left < right:
            mid = left + (right - left)//2
            count = 0
            for num in nums:
                # 统计小于等于mid值的数的个数
                if num <= mid:
                    count += 1
            # 当统计得到的数小于等于中间数,则意味着在mid的右侧查找
            if count <= mid:
                left = mid + 1
            else:
                right = mid
        return left
367. 有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
进阶:不要 使用任何内置的库函数,如 sqrt 。

输入:num = 16
输出:true

  • 使用二分法查找答案
class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        left = 0
        right = num
        while left < right:
            mid = left + (right - left)//2
            if mid*mid < num:
                left = mid + 1
            elif mid*mid > num:
                right = mid - 1
            else:
                left = mid
                break
        return left*left == num
167. 两数之和 II - 输入有序数组

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

  • 双指针,对于一个非递减序列,从两端向中间查找
class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left = 0
        right = len(numbers) - 1
        # 注意left不能等于right,不可以使用相同的元素
        while left < right:
            if numbers[left] + numbers[right] == target:
                return [left+1, right+1]
            elif numbers[left] + numbers[right] < target:
                left += 1
            else:
                right -= 1
33. 搜索旋转排序数组

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4(下标)

# 遍历查找
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        for i in range(len(nums)):
            if nums[i] == target:
                return i
        return -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
            # 1 先将旋转数组看作是左右两个递增的数组
            # 首先需要判断mid索引位置的数在左右哪一个数组
            # 再判断target在左右那个数组
            if nums[mid] >= nums[left]:
                # mid属于左边数组
                if nums[mid] > target and nums[left] <= target:
                    # target在左边数组
                    right = mid - 1
                else:
                    # target在右边数组
                    left = mid + 1
            elif nums[mid] :
                # mid在右侧数组
                if nums[mid] < target and nums[right] >= target:
                    # target在右边数组
                    left = mid + 1
                else:
                    # target在右边数组
                    right = mid - 1
        return -1
81. 搜索旋转排序数组 II

输入:nums = [2,5,6,0,0,1,2], target = 0
输出:true

# 方法1:直接判断目标值是否在数组中,等同于遍历查找
class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        return target in nums

# 方法2:二分法 利用原来数组的有序性
class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        n = len(nums)
        if n == 0:
            return False

        left = 0
        right = len(nums) - 1
        while left < right:
            mid = left + (right - left)//2

            # 1 mid在左边的数组
            if nums[mid] > nums[left]:
                # target在左边的数组
                if nums[mid] >= target and nums[left] <= target:
                    right = mid
                else:
                    left = mid + 1
            # 2 mid在右边的数组
            elif nums[mid] < nums[left]:
                # target在右边的数组
                if nums[mid] < target and nums[right] >= target:
                    left = mid + 1
                else:
                    right = mid
            else:
                if nums[mid] == target:
                    return True
                else:
                    left += 1
        return nums[left] == target
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容