二分查找题集

5489. 两球之间的磁力

在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n 个空的篮子,第 i 个篮子的位置在 position[i] ,Morty 想把 m 个球放到这些篮子里,使得任意两球间 最小磁力 最大。

已知两个球如果分别位于 x 和 y ,那么它们之间的磁力为 |x - y| 。

给你一个整数数组 position 和一个整数 m ,请你返回最大化的最小磁力。

1.202场周赛3AC,第三题这题一开始就想二分,没想出来,先做了第四题,可惜的AK机会。。二分一生的天敌

分析:

答案是求任意两点间的最小距离最大化,最小距离:1,最大距离:arr[-1]-arr[0],check函数用于检查当前的距离是否能满足题目的要求放m个球,然后二分搜索距离即可。

总结:或许可以抽象出一个思维框架,二分的思考流程是,首先确定你要二分东西的区间(上下限),然后去二分这个想要的东西,这里时间复杂度logn,然后再用n的时间去检查是否满足要求!

class Solution:
    def maxDistance(self, position: List[int], m: int) -> int:
        position.sort()
        left=1
        right=position[-1]-position[0]

        def check(diff):
            count,i,j = 1,0,0
            while j=m

        while left<=right:
            mid=(left+right)//2
            if check(mid):
                left=mid+1
            else:
                right=mid-1
        return left-1

面试题 08.03. 魔术索引

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

示例1:

 输入:nums = [0, 2, 3, 4, 5]
 输出:0
 说明: 0下标的元素为0

1.不是真正的二分,因为数组不是严格的单调增加

class Solution:
    def findMagicIndex(self, nums: List[int]) -> int:
        if not nums: return -1
        if nums[0]==0:return 0
        p, n = 0, len(nums)
        while p < n:
            if nums[p] > p: p = nums[p]
            elif nums[p] == p: return p
            else: p += 1
        return -1
        

面试题 10.05. 稀疏数组搜索

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

示例1:

 输入: words = ["at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""], s = "ta"
 输出:-1
 说明: 不存在返回-1。
示例2:

 输入:words = ["at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""], s = "ball"
 输出:4

1.不是真正的递增序列,问题是当遇到arr[mid]==""时要知道left和right怎么变化,这里处理的方法是遇到空的时候线性扫描

class Solution:
    def findString(self, words: List[str], s: str) -> int:
        left=0
        right=len(words)-1
        while left<=right:
            mid=(left+right)//2
            if words[mid]=="":
                if words[right]==s:return right  #左扫 右扫都可以
                right-=1
            elif words[mid]==s:return mid
            elif words[mid]>s:right=mid-1
            else:left=mid+1
        return -1

剑指 Offer 53 - I. 在排序数组中查找数字 I

统计一个数字在排序数组中出现的次数。

 

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

1.二分中带线性扫描,极端情况会退化到o(n)

2.2个二分,一个确定上边界,一个确定下边界

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

剑指 Offer 11. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。  

示例 1:

输入:[3,4,5,1,2]
输出:1
示例 2:

输入:[2,2,2,0,1]
输出:0

1.二分很灵活,可以多写几组数据先观察清楚!

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

167. 两数之和 II - 输入有序数组

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

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

1.双指针o(n)

2.二分,对每一个数作为num,然后二分的去查找target-num,o(nlogn)

class Solution(object):
    def twoSum(self, numbers, target):
        """
        :type numbers: List[int]
        :type target: int
        :rtype: List[int]
        """
        if not numbers:
            return []
        left = 0 
        right = len(numbers) - 1
        while left < right:
            if numbers[left]+numbers[right]==target:
                return [left+1,right+1]
            elif numbers[left]+numbers[right]>target:
                right -= 1
            else:
                left += 1
        return 
class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        n=len(numbers)
        for i in range(n):
            left=i+1
            right=n-1
            while left<=right:
                mid=(right+left)//2
                if numbers[mid]==target-numbers[i]:return [i+1,mid+1]
                elif numbers[mid]>target-numbers[i]:right=mid-1
                else:left=mid+1
        return [-1,-1]

744. 寻找比目标字母大的最小字母

给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。

在比较时,字母是依序循环出现的。举个例子:

如果目标字母 target = 'z' 并且字符列表为 letters = ['a', 'b'],则答案返回 'a'

1.二分真的很灵活,很显然当arr[mid]<=target是left=mid+1,但arr[mid]>target时如果right=mid-1,假如mid正好是第一个大于target的数不就被跳过了么,虽然跳过了,但最后结束时left=mid+1又加回来了!

class Solution:
    def nextGreatestLetter(self, letters: List[str], target: str) -> str:
        left=0
        right=len(letters)-1
        while left<=right:
            mid=(right+left)//2
            if letters[mid]<=target:
                left=mid+1
            else:
                right=mid-1
        return letters[left] if left

 

你可能感兴趣的:(Leetcode)