目录

leetcode-二分

leetcode-二分

二分查找704

leetcode 75

leetcode 374

二分适用于整数 且直到待查找范围

from bisect import bisect_right #导入二分库

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

left和right 求mid

nums[mid] < target ,小了,要大点,在右边,左指针往右

nums[mid] > target ,大了,要小点,在左边,右指针往左

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        # 先生成左指针和和右指针
        left,right=0,len(nums)-1
        while(left <= right): #结束条件为left>right
            mid=(right-left)//2 +left #求出mid为中间值
            num=nums[mid]
            if(num == target):
                return mid
            elif num<target: #小于target了,num要大一点 ,在右边
                left=mid+1 #在右边,左指针要往右
            else:
                right=mid-1 #在左边,右指针要往左
        return -1 #找不到
            
输入: nums = [1,3,5,6], target = 5
输出: 2

没找到的话返回应该插入的位置,找不到返回left

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        # 先生成左指针和和右指针
        left,right=0,len(nums)-1
        while(left <= right): #结束条件为left>right
            mid=(right-left)//2 +left #求出mid为中间值
            num=nums[mid]
            if(num == target):
                return mid
            elif num<target: #小于target了,num要大一点 ,在右边
                left=mid+1 #在右边,左指针要往右
            else:
                right=mid-1 #在左边,右指针要往左
        return left #找不到
            
输入: letters = ["c", "f", "j"],target = "a"
输出: "c"
解释:letters 中字典上比 'a' 大的最小字符是 'c'。

from bisect import bisect_right #导入二分库

找不到返回待插入位置

bisect.bisect和bisect.bisect_right返回 大于x的第一个下标 (相当于C++中的upper_bound)

bisect.bisect_left返回 大于等于x的第一个下标 (相当于C++中的lower_bound)。

https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=E%3A%5Cvscode%5Cc%5Cacm%5Cpic%5Cassets%5Cimage-20240320110857590.png&pos_id=img-KKYb6wzS-1741683577195

j = bisect.bisect_right(rides, start, hi = i - 1, key = lambda x : x[1])

1

rides:这是一个列表,已经按顺序排列。

start:这是我们要在rides中查找的元素。

hi = i - 1:这是查找的上界。具体而言,在[0, hi)这个左闭右开区间中,寻找满足rides[x][1] > start且x最小的case,返回x。如果没有找到,则返回hi。

key = lambda x : x[1]:这是用于比较的元素。默认情况下,bisect_right比较元素x的值,而这里表示比较元素x的下标等于1的元素的值(说明元素x是一个列表或元组等等),即x[1]。

from bisect import bisect_right
class Solution(object):
    def nextGreatestLetter(self, letters, target):
        """
        :type letters: List[str]
        :type target: str
        :rtype: str
        """
        return letters[bisect_right(letters,target)] if target<letters[-1] else letters[0]

或者使用next函数

next(iterator,default)

iterator表示迭代器

default表示迭代器耗尽的默认值

https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=E%3A%5Cvscode%5Cc%5Cacm%5Cpic%5Cassets%5Cimage-20240320112606726.png&pos_id=img-RRcPjQsd-1741683577197 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=E%3A%5Cvscode%5Cc%5Cacm%5Cpic%5Cassets%5Cimage-20240320112725664.png&pos_id=img-Rv3Oj1QR-1741683577197

加个if直接是第一个大于3的,>=3就是返回>=3的

https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=E%3A%5Cvscode%5Cc%5Cacm%5Cpic%5Cassets%5Cimage-20240320112900107.png&pos_id=img-zChsiWnZ-1741683577198

加个找不到next的默认值,就返回第一个

https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=E%3A%5Cvscode%5Cc%5Cacm%5Cpic%5Cassets%5Cimage-20240320113218777.png&pos_id=img-f0bDtEuW-1741683577198

class Solution(object):
    def nextGreatestLetter(self, letters, target):
        """
        :type letters: List[str]
        :type target: str
        :rtype: str
        """
        # return letters[bisect(letters,target)] if target<letters[-1] else letters[0]
        return next((letter for letter in letters if letter>target),letters[0])
        
输入:grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]
输出:8
解释:矩阵中共有 8 个负数。

找负数个数

#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved 
#
# @Time    : 2024/3/20 11:35
# @Author  : Cry
# @File    : 统计负数.py
# @IDE     : PyCharm
# 输入为递减的m*n数列
import bisect


class Solution(object):
    # 二分
    def countNegatives(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        res=0
        for list in grid:
            left,right=0,len(list)-1
            pos=-1
            while(left<=right):
                mid=(right-left)//2+left
                if(list[mid] < 0): #如果当前小于0,那么就要还往左边找
                    pos=mid
                    right=mid-1
                else:
                    left=mid+1 #否则往右边
            if(pos!=-1):
                res+=len(list)-1-pos+1
        return res
    #暴力破解
    def two(self,grid):
        res = 0
        for list in grid:
            for i in range(len(list)):
                if list[i] < 0:
                    res += len(list) - 1 - i + 1
                    break
        return res
print(Solution().countNegatives([[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]))
print(Solution().two([[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]))

分治和动态规划:

分治:小问题没有关联

动态规划:小问题-中问题-大问题,套环

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

找target在数组的第一个和最后一个位置

#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved 
#
# @Time    : 2024/3/20 21:50
# @Author  : Cry
# @File    : 元素的第一个和最后一个位置.py
# @IDE     : PyCharm
# 找第一次和最后一次出现的位置,没有的话返回-1
class Solution(object):
    # 二分
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        lens=len(nums)
        if(lens==0):
            return [-1,-1]
        firstPosition=self.findFirstPosition(nums,target)
        if firstPosition==-1:
            return [-1,-1]
        lastPosition=self.findLastPosition(nums,target)
        return [firstPosition,lastPosition]
    def findFirstPosition(self,nums,target):
        left=0
        right=len(nums)-1
        while(left<right):
            mid=(left+right) >> 1
            if(nums[mid]<target):
                left=mid+1
            elif nums[mid]==target:
                #[left,mid]
                right=mid
            else:#[left,mid-1]
                right=mid-1
        if nums[left]==target:
            return left
        else:
            return -1

    def findLastPosition(self,nums,target):
        left=0
        right=len(nums)-1
        while(left<right):
            mid=(right+left+1)>>1
            if(nums[mid]<target):
                left=mid+1
            elif nums[mid]==target:
                # [mid,right]
                left=mid
            else:
                right=mid-1
        return left
    #暴力
    def searchRange_baoli(self,nums,target):
        res=[]
        for i in range(len(nums)):
            if nums[i]==target:
                res.append(i)

        if len(res)==0:
            return [-1,-1]
        else:
            return [res[0],res[-1]]
# 优化二分
    def searchRange2(self,nums,target):
        if(len(nums)==0):
            return [-1,-1]
        left,right=0,len(nums)-1
        while(left<=right):
            mid=(left+right)//1
            if nums[mid]<target:
                left=mid+1
            elif nums[mid]>target:
                right=mid-1
            else:
                left,right=mid,mid
                while left >0 and nums[left-1]==target:
                    left-=1
                while right<len(nums)-1 and nums[right+1]==target:
                    right+=1
                return [left,right]
        return [-1,-1]
    #暴力 用index
    def search_index(self,nums,target):
        try:
            return [nums.index(target), nums.index(target)+nums.count(target)-1]
        except:
            return [-1,-1]
print(Solution().searchRange_baoli([5,7,7,8,8,10],8))
print(Solution().searchRange([5,7,7,8,8,10],8))
print(Solution().searchRange2([5,7,7,8,8,10],8))
print(Solution().searchRange2([1],1))
print(Solution().search_index([5,7,7,8,8,10],8))

寻找右区间

输入:intervals = [[1,2]]
输出:[-1]
解释:集合中只有一个区间,所以输出-1。
输入:intervals = [[3,4],[2,3],[1,2]]
输出:[-1,0,1]
解释:对于 [3,4] ,没有满足条件的“右侧”区间。
对于 [2,3] ,区间[3,4]具有最小的“右”起点;
对于 [1,2] ,区间[2,3]具有最小的“右”起点。
#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved
#
# @Time    : 2024/3/21 11:59
# @Author  : Cry
# @File    : 寻找有区间.py
# @IDE     : PyCharm
#
import bisect


class Solution:
    def findRightInterval(self,intervals):
        for i,interval in enumerate(intervals): #[[2,4],[1,3]]

            interval.append(i) # 1 [2,4,0] [1,3,1]
        intervals.sort() #[[1,3,1],[2,4,2]]
        print(intervals)

        n=len(intervals)
        ans=[-1]*n
        for _,end,id in intervals: #start end index
            i=bisect.bisect_left(intervals,[end]) #二分找end
            # 找[[1, 2, 2], [2, 3, 1], [3, 4, 0]],end为2 3 4
            # 结果为找2,找每个list第一个数:[2,3,1] :1
            # 结果为找3,找每个list第一个数:[3,4,0] :2
            # 结果为找4,找每个list第一个数:找不到,插入到最后
            print(i)
            if i<n:
                ans[id]=intervals[i][2]
        return ans
    #指针
    def findRightInterval2(self, intervals):
        n = len(intervals)
        starts, ends = list(zip(*intervals)) #*intervals用来元素解压缩并作为参数传递给zip函数
        starts = sorted(zip(starts, range(n)))
        ends = sorted(zip(ends, range(n)))

        ans, j = [-1] * n, 0
        for end, id in ends:
            while j < n and starts[j][0] < end:
                j += 1
            if j < n:
                ans[id] = starts[j][1]
        return ans
print(Solution().findRightInterval([[3,4],[2,3],[1,2]]))

基于时间的键值存储

输入:
["TimeMap", "set", "get", "get", "set", "get", "get"]
[[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
输出:
[null, null, "bar", "bar", null, "bar2", "bar2"]
#!/usr/bin/python3
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2024 - 2024 Cry, Inc. All Rights Reserved 
#
# @Time    : 2024/3/21 12:52
# @Author  : Cry
# @File    : 基于时间的键值存储.py
# @IDE     : PyCharm
import bisect


class TimeMap(object):

    def __init__(self):
        self.data={}
    def set(self, key, value, timestamp):
        """
        :type key: str
        :type value: str
        :type timestamp: int
        :rtype: None
        """
        if key not in self.data:
            self.data[key]=[]
        self.data[key].append((timestamp,value))


    def get(self, key, timestamp):
        """
        :type key: str
        :type timestamp: int
        :rtype: str
        """
        if key in self.data:
            arr=self.data[key]
            idx = bisect.bisect_right(arr, (timestamp, chr(127)))
            return "" if idx == 0 else arr[idx - 1][1]
        return ""

commands = ["TimeMap","set","get","get","get","get","set","get","get"]
inputs = [[],["ljfvbut","tatthnvvid",3],["ljfvbut",4],["ljfvbut",5],["ljfvbut",6],["ljfvbut",7],["eimdon","pdjbnnvje",8],["eimdon",9],["eimdon",10]]

output = []
obj = None
for i in range(len(commands)):
    if commands[i] == "TimeMap":
        obj = TimeMap()
        output.append(None)
    elif commands[i] == "set":
        obj.set(*inputs[i])
        output.append(None)
    elif commands[i] == "get":
        output.append(obj.get(*inputs[i]))

print(output)


# Your TimeMap object will be instantiated and called as such:

快照数组

输入:["SnapshotArray","set","snap","set","get"]
     [[3],[0,5],[],[0,6],[0,0]]
输出:[null,null,0,null,5]
class SnapshotArray(object):

    def __init__(self, length):
        """
        :type length: int
        """
        self.data=[[(0,0)] for _ in range(length)]
        self.snap_id=0


    def set(self, index, val):
        """
        :type index: int
        :type val: int
        :rtype: None
        """
        self.data[index].append((self.snap_id,val))


    def snap(self):
        """
        :rtype: int
        """
        self.snap_id+=1
        return self.snap_id-1


    def get(self, index, snap_id):
        """
        :type index: int
        :type snap_id: int
        :rtype: int
        """
        i=bisect.bisect_left(self.data[index],(snap_id+1,))-1
        return self.data[index][i][1]



# Your SnapshotArray object will be instantiated and called as such:
# obj = SnapshotArray(length)
# obj.set(index,val)
# param_2 = obj.snap()
# param_3 = obj.get(index,snap_id)

搜索旋转数组

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
class Solution(object):
    # 二分
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        if not nums:
            return -1
        left,right=0,len(nums)-1
        while left<=right:
            mid=(left+right)>>1
            if nums[mid]==target:
                return mid
            if nums[0]<=nums[mid]:
                if nums[0]<=target<nums[mid]:
                    right=mid-1
                else:
                    left=mid+1
            else:
                if nums[mid]<target<=nums[len(nums)-1]:
                    left=mid+1

                else:
                    right=mid-1
        return -1
    # 暴力
    def search2(self, nums, target):
        if target in nums:
            for i in nums:
                if i == target:
                    return nums.index(i)
        else:
            return -1

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

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

找到最小值:min(nums)

为了提升速度:

用二分:

class Solution:
    def findMin(self, nums: List[int]) -> int:
        left, right = -1, len(nums) - 1  # 开区间 (-1, n-1)
        while left + 1 < right:  # 开区间不为空
            mid = (left + right) // 2
            if nums[mid] < nums[-1]:  # 蓝色
                right = mid
            else:  # 红色
                left = mid
        return nums[right]

# 作者:灵茶山艾府
# 链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/solutions/1987499/by-endlesscheng-owgd/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

有重复值的找最小值:

def findMin2(nums):
    n=len(nums)
    l,r=-1,n-1
    while (l+r<r):
        m=(l+r)//2
        if(nums[m]>nums[r]):
            l=m
        elif(nums[m]<nums[r]):
            r=m
        else:
            r-=1 #相等的话右边减一
    return nums[r]
    

猜数字大小

a=4
def guess(n):
    if n>a:
   		return 1
    elif n<a:
        return -1
    else:
        return 0
def guessNumber(n):
    left,right=1,n
    while(left<right):
        mid=(right-mid)//2
        g=guess(mid)
        if(g==1): #大了,左指针往mid来
            left=mid+1
        elif g==-1:
            right=mid
        else:
            return mid
    return left
            

第一个错误的版本

找到第一个出错的版本:

isBadVersion?() 1 2 3 4

​ f f t t

所以第一个错误版本为3

def firstBadVersion(n):
    left,right=0,n
    while(left<=right):
        mid=(right-left)//2+left
        if(isBadVersion(mid)):
            right=mid-1
        else:
            left=mid+1
    return left

lse:

return 0

def guessNumber(n):

left,right=1,n

while(left<right):

mid=(right-mid)//2

g=guess(mid)

if(g 1): #大了,左指针往mid来

left=mid+1

elif g -1:

right=mid

else:

return mid

return left


> 第一个错误的版本
>
> 找到第一个出错的版本:
>
> isBadVersion?()   1 2 3 4
>
> ​			       f f  t  t
>
> 所以第一个错误版本为3
>
> 

```python
def firstBadVersion(n):
    left,right=0,n
    while(left<=right):
        mid=(right-left)//2+left
        if(isBadVersion(mid)):
            right=mid-1
        else:
            left=mid+1
    return left