• 五、二分法


    1. 二分迭代程序(找到的位置可能不是第一次出现的位置)

    def bi_search_iter(alist, item):
        left, right = 0, len(alist) - 1
        while left <= right:
            mid = (left + right) // 2
            if alist[mid] < item:
                left = mid + 1
            elif alist[mid] > item:
                right = mid - 1
            else: 
                return mid 
        return -1 
    

    2.  二分推荐模板(能确保找到的元素是第一次出现的位置)

    def binarysearch(alist, item):
        if len(alist) == 0:
            return -1
        
        left, right = 0, len(alist) - 1
        while left + 1 < right:  # (1) left == right   (2) left + 1 == right,即左指针和右指针相邻
            mid = left + (right - left) // 2
            if alist[mid] == item:
                right = mid # 确保找到的是第一个元素  如在[1, 1, 2, 2, 2, 2,3, 4] 里面找2的出现的位置,这句话就能保证找到第一个2出现的位置。
            elif alist[mid] < item:
                left = mid
            elif alist[mid] > item:
                right = mid
        
        if alist[left] == item:  # 当最后的左指针和右指针相邻时,有可能left和right所指的元素都为2,此时left指针所指的位置为答案  
            return left
        if alist[right] == item:
            return right
        
        return -1
    

     3. leetcode第153题. 寻找旋转排序数组中的最小值

    # O(nlogn)
    def searchlazy(alist):
        alist.sort()
        return alist[0]
    # O(n)
    def searchslow(alist):
        mmin = alist[0]
        for i in alist:
            mmin = min(mmin, i)
        return mmin 
            
    # O(lgn)
    def search(alist):
        if len(alist) == 0:
            return -1    
        left, right = 0, len(alist) - 1
        while left + 1 < right: 
            if (alist[left] < alist[right]): # 排好序的情况 
                return alist[left]
            mid = left + (right - left) // 2
            if (alist[mid] >= alist[left]): # 拐点出现在后半部分 如 [3,4,5,1,2] 
                left = mid + 1
            else: # 拐点出现在前半部分 9如[7,8,9,1,2,3,4,5,6]
                right = mid 
        return alist[left] if alist[left] < alist[right] else alist[right]
    

    4.  leetcode第33题. 搜索旋转排序数组

    def search(alist, target):
        if len(alist) == 0:
            return -1    
        left, right = 0, len(alist) - 1
        while left + 1 < right: 
            mid = left + (right - left) // 2
            if alist[mid] == target:
                return mid
            
            if (alist[left] < alist[mid]): # 拐点出现在前半部分 如 [3,4,5,1,2]  说明前半部分是排好序的 [1,3,5]
                if alist[left] <= target and target <= alist[mid]: # 如果目标值出现在此排好序的范围内,我就在这里面查找
                    right = mid
                else: # 否则我去后面没有排好序的查找
                    left = mid
            else: # 拐点出现在后半部分 如[7,8,9,1,2,3,4,5,6],说明后半部分是排好序的
                if alist[mid] <= target and target <= alist[right]: 
                    left = mid
                else: 
                    right = mid
                                
        if alist[left] == target:
            return left
        if alist[right] == target:
            return right
            
        return -1
    

    5.  leetcode第35题. 搜索插入位置

    def search_insert_position(alist, target):
        if len(alist) == 0:
            return 0  
        left, right = 0, len(alist) - 1
        while left + 1 < right: 
            mid = left + (right - left) // 2
            if alist[mid] == target:
                return mid
            
            if (alist[mid] < target):
                left = mid
            else:
                right = mid
                
        if alist[left] >= target: # 如【1,3,5,6】插入0或1  
            return left
        if alist[right] >= target: # 如【1,3,5,6】插入2,4 
            return right
            
        return right + 1 # 如[1,3,5,6] 插入7 
    

    6.  leetcode 第34题. 在排序数组中查找元素的第一个和最后一个位置 难度:中等

    def search_range(alist, target):
        if len(alist) == 0:
            return (-1, -1)  
        
        lbound, rbound = -1, -1
    
        # search for left bound  # 搜索该元素出现的第一个位置
        left, right = 0, len(alist) - 1
        while left + 1 < right: 
            mid = left + (right - left) // 2
            if alist[mid] == target:
                right = mid
            elif (alist[mid] < target):
                left = mid
            else:
                right = mid 
                
        if alist[left] == target: 
            lbound = left 
        elif alist[right] == target: 
            lbound = right
        else:
            return (-1, -1)
    
        # search for right bound  搜索该元素出现的最后一个位置 
        left, right = 0, len(alist) - 1        
        while left + 1 < right: 
            mid = left + (right - left) // 2
            if alist[mid] == target:
                left = mid
            elif (alist[mid] < target):
                left = mid
            else:
                right = mid
                
        if alist[right] == target:
            rbound = right
        elif alist[left] == target:
            rbound = left
        else:
            return (-1, -1)        
            
        return (lbound, rbound)
    

    7.  Search 1st Position of element in Infinite Array(数据流中寻找元素出现的第一个位置)

    def binarysearch(alist, item):
        if len(alist) == 0:
            return -1
        
        left, right = 0, len(alist) - 1
        while left + 1 < right:
            mid = left + (right - left) // 2
            if alist[mid] == item:
                right = mid
            elif alist[mid] < item:
                left = mid
            elif alist[mid] > item:
                right = mid
        
        if alist[left] == item:
            return left
        if alist[right] == item:
            return right
        
        return -1 
    
    # 倍增法:长度两倍两倍的增加   
    def search_first(alist, target):
        left, right = 0, 1
        
        while alist[right] < target:
            left = right
            right *= 2
            
            if (right > len(alist)):
                right = len(alist) - 1
                break
        
        return left + binarysearch(alist[left:right+1], target)
    

    8.  leetcode第475题. 供暖器

    ** Solution **
    1、找到每个房屋离加热器的最短距离(即找出离房屋最近的加热器)。 2、在所有距离中选出最大的一个max(res)即为结果。

    from bisect import bisect
    
    def findRadius(houses, heaters):
        heaters.sort()
        ans = 0
        # 对于每一个房子而言,要找到它左边的最近的供暖设备和它右边的最近的供暖设备
        for h in houses:
            hi = bisect(heaters, h)  # bisect函数返回的是插入索引的位置
            left = heaters[hi-1] if hi - 1 >= 0 else float('-inf')
            right = heaters[hi] if hi < len(heaters) else float('inf')
            ans = max(ans, min(h - left, right - h))
    
        return ans
    

    9.  leetcode 第69题. x 的平方根

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

    10.  leetcode 74. 搜索二维矩阵

    编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

    每行中的整数从左到右按升序排列。 每行的第一个整数大于前一行的最后一个整数。

    # Naive: 每一行使用二分搜索寻找 O(nlgm)   每一列使用二分搜索 O(mlgn)
    # 法一:二分搜索 O(lgmn)
    # 解释: https://leetcode-cn.com/problems/search-a-2d-matrix/solution/sou-suo-er-wei-ju-zhen-by-leetcode/
    def search_matrix(matrix, target):
        if len(matrix) == 0:
            return False
        if len(matrix[0]) == 0:
            return False 
        
        m = len(matrix)
        n = len(matrix[0])
        left, right = 0, m*n-1  # 看成一个一维的有序数组
        while left + 1 < right:          
            mid_idx = left + (right - left) // 2
            mid_element = matrix[mid_idx // n][mid_idx % n]
            if mid_element == target:
                return True 
            elif mid_element < target:
                left = mid_idx 
            else:
                right = mid_idx 
                
        if matrix[left//n][left%n] == target:
            return True
        if matrix[right//n][right%n] == target:
            return True
        return False 
    
    # 法二:从矩阵的左上角或者右下角搜索 O(m + n)
    def search_matrix(matrix, target):
        if len(matrix) == 0:
            return False
        if len(matrix[0]) == 0:
            return False 
        
        m = len(matrix) - 1  # 行数
        n = len(matrix[0]) - 1   # 列数 
        
        x,y = m, 0 
        while(x >= 0 and y <= n):
            if matrix[x][y] > target:
                x -= 1 
            elif matrix[x][y] < target:
                y += 1 
            else:
                return True 
        return False 
    

    11. leetcode: 第378题. 有序矩阵中第K小的元素 难度:中等

    给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。 请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。
    
    首先第k小数一定落在[l, r]中,其中l = matrix[0][0], r = matrix[row - 1][col - 1]. 我们二分值域[l, r]区间,mid = (l + r) // 2, 对于mid,我们检查矩阵中有多少元素小于等于mid, 记个数为cnt,那么有:
    
    1、如果cnt < k, 那么[l, mid]中包含矩阵元素个数一定小于k,那么第k小元素一定不在[l, mid] 中,必定在[mid + 1, r]中,所以更新l = mid + 1.
    
    2、否则cnt >= k,那么[l, mid]中包含矩阵元素个数就大于等于k,即第k小元素一定在[l,mid]区间中, 更新r = mid; 
    
    from bisect import bisect
    def kthSmallest(matrix, k):
        lo, hi = matrix[0][0], matrix[-1][-1]
        while lo < hi:
            mid = lo + (hi - lo) // 2
            if sum(bisect(row, mid) for row in matrix) < k: 
                lo = mid + 1
            else:
                hi = mid
        return lo
    

    12. leetcode 第287题. 寻找重复数 难度:中等

    # 二分法的思路是先猜一个数(有效范围 [left, right]里的中间数 mid),
    #然后统计原始数组中小于等于这个中间数的元素的个数 cnt,
    #如果 cnt 严格大于 mid,(注意我加了着重号的部分「小于等于」、
    #「严格大于」)。根据抽屉原理,重复元素就在区间 [left, mid] 里;
    
    #链接:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/er-fen-fa-si-lu-ji-dai-ma-python-by-liweiwei1419/
    
    # index:  1    2    3    4    5 
    # value:  1    3    4    2    2  
    # 二分法 O(nlgn) 
    def findDuplicate(nums):
        low = 1
        high = len(nums)-1
    
        while low < high:
            mid = low + (high - low) // 2 
            count = 0
            for i in nums:
                if i <= mid:
                    count += 1
            if count <= mid: # 重复元素在[mid, right]区间里面 
                low = mid+1
            else:  # 重复元素在[left, mid]区间里面 
                high = mid
        return low
    

      

  • 相关阅读:
    围棋角部大眼的气
    openwrt 增加自定义包
    OpenWrt – 编译前的./scripts/feeds脚本用法
    memset 赋初值为无穷大和无穷小
    android 自动点击学习课程
    最多硬币问题
    最少硬币问题
    9.2 嵌套矩形问题
    ajax请求到一个controller在跳转到下载的controller上也不能下载,百度了一下总结下原因:发现原来jQuery的ajax回调已经把response的数据傻瓜式的以字符串的方式解析.
    MySQL 数据库的远程访问权限
  • 原文地址:https://www.cnblogs.com/carlber/p/14231921.html
Copyright © 2020-2023  润新知