zoukankan      html  css  js  c++  java
  • 查找算法:二分法查找及其python实现案例

    承接上一篇:查找:顺序查找与二分法查找,将二分法更多详细的python实现解题写下笔记。

    简单方法

    '''
    二分法查找在列表中的用户输入值,返回index
    三种情况跳出循环体: LR相邻    LR位置重合   RL
    算法时间复杂度为O(logn)
    '''
    
    def bi_search(lis,num):
        
        if len(lis) == 0:           #判断边界条件
            return -1
      
        left, right = 0, len(lis)-1          #列表的起始点和终点
      
        while left <= right:
    
            mid = (left + (right - left)) // 2     #取二分中心点,或 mid = (left + right) // 2
    
            if num < lis[mid]:               #右点左移
                right = mid - 1 
            elif num > lis[mid]:             #左点右移
                left = mid + 1 
            else:                            #num == lis[mid]    
                return mid
    
        return -1
    
    num = int(input('please input a num:'))
    alist = [1,2,3,4,5,6,7,8]
    bi_search(alist,num)
    
    输出结果:
    please input a num:3
    2    

    改进方案:如果我要找一个数组中相同数字最前面的那个数字。比如lis = [1,2,2,2,2,3,3,5,6],用上面的代码会返回index为4,下面代码解决如何返回相同元素的最前一个

    经典方法

    '''
    改进版本经典方法,这种方法更加好写
    把常规方法的right = mid -1 和left = mid +1修改
    找不到返回-1
    '''
    
    def bi_search2(lis, num):
       
        #这个程序中这一条判断边界非常重要
        if len(lis) == 0:
            return -1
        
        #判断二分条件
        left, right = 0, len(lis) - 1
        while left + 1 < right:
            mid = left + (right - left) // 2
            if lis[mid] == num:
                right = mid
            elif lis[mid] < num:
                left = mid
            elif lis[mid] > num:
                right = mid
        
        #在这里返回
        if lis[left] == num:
            return left
        if lis[right] == num:
            return right
        
        return -1
    
    
    num = int(input('please input a num:'))
    alist = [1,2,2,2,2,6,7,8]
    bi_search2(alist,num)
    
    #输出结果
    please input a num: 2
    1

    下面类型有针对旋转数组的搜索方法,先来介绍一下旋转数组的定义:

    旋转数组:

    将包含 n 个元素的数组向右旋转 步;例如,如果  n = 7 ,  k = 3,给定数组  [1,2,3,4,5,6,7]  ,向右旋转后的结果为 [5,6,7,1,2,3,4]

    以下为更多的使用二分检索的类型:

    查找旋转数组中的最小值

    '''
    查找旋转数组中的最小值
    python自带min()算法时间复杂度为O(n)
    比这个更好的时间复杂度方法就是二分法了O(lgn)
    '''
    
    #O(nlgn)
    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]):
                left = mid + 1
            else:
                right = mid
        
        #第四步
        return alist[left] if alist[left] < alist[right] else alist[right]
    
    num = int(input('please input a num:'))
    alist = [3,4,5,6,7,1,2]
    search(alist)
    
    #输出结果
    please input a num:4
    1

    查找旋转数组的指定值target

    '''
    查找旋转数组的指定值target
    O(lgn)
    '''
    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
            
            #判断当mid前半部分排好序的
            if (alist[left] < alist[mid]):
                #判断target的位置决定mid的赋值
                if alist[left] <= target and target <= alist[mid]:
                    right = mid
                else:
                    left = mid
            #mid后半部分
            else:
                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
    
    num = int(input('please input a num:'))
    alist = [3,4,5,6,7,1,2]
    search(alist,num)
    
    #输出结果
    please input a num:4
    1

    搜索插入位置

    '''
    搜索插入位置
    给定一个有序数组和目标值,查找数组该值返回Index,否则返回应该被插入的位置Index
    用for循环一个个找为O(n),以下二分法为O(lgn)更优
    '''
    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:
            return left
        if alist[right] >= target:
            return right
            
        return right + 1
    
    num = int(input('please input a num:'))
    alist = [3,4,5,6,7,8,9]
    search_insert_position(alist,num)
    
    #输出结果
    please input a num:4
    1

    搜索一个区间

    '''
    搜索一个区间
    找到一个给定目标值的起始和结束位置
    '''
    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)
    
    num = int(input('please input a num:'))
    alist = [3,4,5,6,7,8,9,10,14,15]
    search_range(alist,num)
    
    #输出结果
    please input a num:6
    (3, 3)

    在无限序列中找到某元素出现的第一个位置

    '''
    在无限序列中找到某元素出现的第一个位置
    '''
    
    def search_first(alist):
        left, right = 0, 1
        
        while alist[right] == 0:
            left = right
            right *= 2
            
            if (right > len(alist)):
                right = len(alist) - 1
                break
        
        #这里为1
        return left + search_range(alist[left:right+1], 1)[0]
    
    alist = [0, 0, 0, 0, 0, 1]
    r = search_first(alist)
    print(r)
    
    #输出结果
    5

    找到重复数

    '''
    给定一个包含N+ 1整数的数组NUM,其中每个整数在1和N之间(包含),证明至少有一个重复的数字必须存在。假设只有一个重复的数字,找到重复的数字。
    注:
    您不必修改数组(假定数组是只读的)。
    你必须只使用常数,O(1)额外的空间。
    您的运行时复杂性应该小于O(n2)。
    在数组中只有一个重复的数字,但是它可以重复不止一次。
    '''
    
    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:
                low = mid+1
            else:
                high = mid
        return low
    
    nums = [3,5,6,3,1,4,2]
    findDuplicate(nums)
    
    #输出结果
    3

     供暖设备案例

    '''
    供暖设备案例
    设计一款有固定供暖半径的供暖设备来给所有房屋供暖
    输入每个房屋和每个供暖设备的位置,输出供暖设备的最小半径
    
    '''
    from bisect import bisect
    
    def findRadius(houses, heaters):
        heaters.sort()
        ans = 0
    
        for h in houses:
            hi = bisect(heaters, h)
            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
    
    houses = [1,2,3,4]
    heaters = [1,4]
    f1 = findRadius(houses, heaters)
    print(f1)
    
    houses2 = [1,12,23,34]
    heaters2 = [12,24]
    f2 = findRadius(houses2, heaters2)
    print(f2)
    
    #输出结果
    1
    11
  • 相关阅读:
    core dump的使用
    wav文件格式
    Unicode编码 【转】
    WAV格式中常见的压缩编码
    两台交换机级联端口mac地址表维护(转载)
    pthread_cond_wait()的使用方法
    makefile自动依赖[转]
    11月的第一天
    再读simpledb 之 事务管理的实现(3)
    再读simpledb 之 元数据管理(1)
  • 原文地址:https://www.cnblogs.com/kumata/p/9131257.html
Copyright © 2011-2022 走看看