二分法-各种应用

二分法

旋转排序数组

寻找旋转排序数组中的最小值

寻找旋转排序数组中的最小值
只需要考虑mid与右边界的情况

class Solution:
    def findMin(self, arr: List[int]) -> int:
        n = len(arr)
        l,r = 0,n-1
        # 3 1 2
        while l<=r:
            mid=l+(r-l)//2
            if arr[mid] < arr[r]:
                r = mid
            else:
                l = mid+1
        return arr[r]

33. 搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

虽然相比较单纯的最大值、最小值查找会麻烦一些(考虑target),但不包含重复数字,问题简化了很多。
每次二分,根据nums[mid]与nums[r]比较得出当前的场景。

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

寻找旋转排序数组中的target,如果有多个,返回最小索引的那个

面试题 10.03. 搜索旋转数组
需要考虑mid、右边界、target的关系。此外,可能包含重复数字,问题更复杂一些
1.先区分递增递减关系。
2.根据递增递减关系找到二分方向。

class Solution:
    def search(self, arr: List[int], target: int) -> int:
        n = len(arr)
        if n==0: return -1
        if arr[0]==target: return 0
        l,r = 0,n-1
        # [3,1,2]
        while l<=r:
            mid=l+(r-l)//2
            # print(mid, arr[mid])
            if arr[mid]==target:
                while mid>0 and arr[mid-1]==arr[mid]:
                    mid -= 1
                return mid
            # mid~r 先递增后递减
            if arr[mid] > arr[r]:
                if arr[mid] <target or target <= arr[r]:
                    l = mid+1
                else:
                    r = mid-1
            # mid~r 递增
            elif arr[mid] < arr[r]:
                if arr[mid] > target or target > arr[r]:
                    r = mid-1
                else:
                    l = mid+1
            else:
                r -= 1
            # print(mid, l,r)
        return -1

查找target

34. 在排序数组中查找元素的第一个和最后一个位置

34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        n = len(nums)
        if n==0: return [-1,-1]
        # 先找最大
        l,r = 0,n-1
        while l<=r:
            mid = l+(r-l)//2
            if nums[mid]<=target:
                l = mid+1
            else:
                r = mid-1
        # print(l,r)
        if nums[r]!=target:
            return [-1,-1]
        ans = [r,r]
        l=0
        while l<=r:
            mid = l+(r-l)//2
            if nums[mid]>=target:
                r = mid-1
            else:
                l = mid+1
        ans[0] = l
        return ans

面试题 10.05. 稀疏数组搜索

稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。

面试题 10.05. 稀疏数组搜索

class Solution:
    def findString(self, words: List[str], s: str) -> int:
        n = len(words)
        l,r = 0,n-1
        # 过滤混淆值,看目标字符串是否位于区间内
        while l<n and words[l]=='':
            l += 1
        if l==n: return -1
        while r>=0 and words[r]=='':
            r -= 1
        if self.lower(s,words[l]) or self.lower(words[r], s):
            return -1
        # 二分查找
        while l<=r:
            mid = l+(r-l)//2
            end = mid
            # 过滤混淆值
            while end>=l and words[end]=='':
                end -= 1
            if end <l:
                l = mid+1
                continue
            # 根据与目标值的比较选择方向及起始点
            if words[end]==s:
                return end
            elif self.lower(s,words[end]):
                r = end-1
            else:
                l = mid+1
        return -1

    def lower(self, s1, s2):
        # s1<s2 返回True
        m,n=len(s1),len(s2)
        for i in range(min(m,n)):
            if s1[i]<s2[i]:
                return True
            elif s1[i]>s2[i]:
                return False
        return m<n

魔术索引

面试题 08.03.魔术索引

面试题 08.03.魔术索引
魔术索引。 在数组A[0…n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。

class Solution:
    def findMagicIndex(self, nums: List[int]) -> int:
        n = len(nums)
        def get(l,r):
            if l>r: return -1
            mid = l+(r-l)//2
            
            ans = get(l,mid-1)
            if ans > -1:
                return ans
            elif nums[mid]==mid:
                return mid
            else:
                return get(mid+1,r)

        left,right=0,n-1
        
        return get(left, right)

查找峰值

162. 寻找峰值

162. 寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞ 。

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        n = len(nums)
        l,r = 0,n-1
        while l<r:
            mid = l+(r-l)//2
            if (mid == 0 or  nums[mid-1]<nums[mid]) and (nums[mid] > nums[mid+1]):
                return mid
            if mid>0 and nums[mid] <=nums[mid-1]:
                r = mid-1
            elif nums[mid]<=nums[mid+1]:
                l = mid+1
        return r

位运算

222. 完全二叉树的节点个数

222. 完全二叉树的节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root: return 0
        t = root
        h = 0
        while t:
            h += 1
            t = t.left
        l, r = 0, (1<<(h-1))-1
        
        while l <= r:
            mid = l+(r-l)//2
            t = root
            for i in range(h-2,-1,-1):
                if mid & (1<<i):
                    t = t.right
                else:
                    t = t.left
            if t:
                ans = t
                l = mid+1
            else:
                r = mid-1
        # print(l,r)
        ans = (1<<(h-1))-1+r+1
        return ans
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值