zoukankan      html  css  js  c++  java
  • 二分查找

    二分查找:每次能够排除掉一半的数据,查找的效率非常高,但是局限性比较⼤,必须是有序序列才可以使⽤二分查找

    要求: 查找的序列必须是有序序列

    精髓: 掐头截尾取中间

    实例: 

     让用户输入一个数字n,判断n是否在给定的列表lst中出现,如果出现请返回n所在的位置.
    # 不使用递归(需要理解和掌握的)-->不改变列表,通过改变左右边界索引去缩小查找范围
    lst = [1, 3, 15, 26, 77, 99, 456, 765, 1234]
    num = int(input('请输入你要查找的元素:'))
    left = 0  # 左边界
    right = len(lst) - 1  # 右边界
    while left <= right:
        # 中间值要放在循环里面计算,因为每次循环中间值都是要根据左右边界而变化的
        mid = (left + right) // 2  # 使用地板除是因为索引只取整数
    
        if num > lst[mid]:  # 比中间值大,在中间值右边,缩小取值范围-->左边界右移到中间值的后面一个元素的位置
            left = mid + 1  # 之所以要+1,是因为中间值已经比较过了
        elif num < lst[mid]:  # 比中间值小,在中间值的左边,缩小取值范围-->右边界左移到中间值的前面一个元素的位置
            right = mid - 1   # 同理,之所以要-1,是因为中间值已经比较过了
        else:  #num == lst[mid]
            print(f'找到了,在索引位置{mid}')
            break
    else:  # 即 left > right  而又没有被break,说明遍历完了整个数据集都没有找到
        print('不存在')
     
    二分查找---非递归算法 
    # 使用递归  法二(需要理解和掌握的)  不改变列表,通过改变左右边界索引去缩小查找范围
    def func(num, lst, left, right):
        if left <= right:
            mid = (left + right) // 2
            if num > lst[mid]:
                left = mid + 1
                return func(num, lst, left, right)  # 递归如果有返回值,所有调用递归的地方必须写return,否则接收到的永远是None
            elif num < lst[mid]:
                right = mid - 1
                return func(num, lst, left, right)
            else:
                print(f'找到了,在索引位置{mid}')
                return mid  # 上面递归函数的前面没有return,这里的值无法被接收到
    
        else:
            print('不存在')
            return -1  # 比如find函数查询不到就返回-1,是有道理的
    
    
    num = int(input('请输入你要查找的元素:'))
    index = func(num, lst, 0, len(lst) - 1)
    print(index)
    普通递归版本二分法
    # 使用递归  通过改变列表去缩小查找范围
    lst = [1, 3, 15, 26, 77, 99, 456, 765, 1234]
    def func(num, lst):
        left = 0
        right = len(lst) - 1
        if lst != []:  # 与left <= right 是等价的,只要列表非空,就满足这个等式
            mid = (left + right) // 2  # 列表非空再去比较,再计算mid  这个放在if的里面和外面不影响结果,逻辑上放在里面更好
            if num > lst[mid]:  # 大于中间值,对列表做切片,取中间值的右侧数据作为新的查找范围
                lst = lst[mid+1:]
                func(num, lst)  # 调用函数自身,再次执行重复的操作
            elif num < lst[mid]:  # 小于中间值,对列表做切片,取中间值的左侧数据作为新的查找范围
                lst = lst[:mid]  # 切片的语法,mid取不到,所以不需要传入 mid-1
                func(num, lst)  # 调用函数自身,再次执行重复的操作
            else:
                print('找到了')  # 这里改变了原来的查找对象lst,就不能再输出所查找的元素的具体位置了,如果存在的话,最后一定是索引0位置
                return   # 为了结束函数,就本题而言写和不写是等价的(因为函数中没有接在这个位置后面要执行的代码了),但是还是写吧,代表函数的结束(也是递归的出口),其他时候(比如函数中这个位置的下面还有代码)就是一定要写才能结束函数的执行了.
        else:  # 遍历完了,不存在
            print('不存在')
            return   # 结束函数,理由同上
    
    num = int(input('请输入你要查找的元素:'))
    func(num, lst)
    另类二分法, 很难计算位置

    拓展:

    # 最高效的查找-->将要查找的元素作为索引,去新列表中找对应的值,看值是不是1,是就存在,不是就不存在
    lst = [1, 23, 115, 26, 7, 99]  # 不需要是有序的
    # 准备工作:构建一个新列表,长度是旧列表的最大值,旧列表的元素作为索引所在位置的元素是1,其他位置都是0
    # 创建空列表
    new_lst = []
    # print(max(lst))  # 获取列表元素的最大值
    # 添加max(lst)个元素,都是0
    for i in range(max(lst) + 1):  # 不+1的话最大值取不到
        new_lst.append(0)  # 添加进去列表最大值个0---> 即构建一个新列表,里面的元素都是0,元素个数是旧列表的最大值
    
    # 遍历旧列表,将旧列表的元素作为索引,将新列表该索引对应位置的元素改成1
    for i in lst:
        new_lst[i] = 1
    print(new_lst)
    
    # 准备工作完成,下面要开始查找了
    n = int(input('请输入要查找的数据:'))
    # 将要查找的元素作为索引,如果新列表该索引对应位置的元素是1,则要查找的元素存在,否则(是0)不存在
    if new_lst[n] == 1:
        print('找到了')  # 只需要查找一次
    else:
        print('不存在')
    # 上面这个的时间复杂度是1,空间复杂度也很低,因为新列表的元素都是0和1,所占用的内存是很小的
    最快的查找

  • 相关阅读:
    PCB 铺铜 转载
    VC++ 学习笔记3 获取编辑框字符串
    VC++ 学习笔记2 列表框添加字符串
    VC++组合框——学习笔记1(组合框选项的添加和无法显示下拉选项)
    微信蓝牙ble记录
    最近遇到的问题与分析还有可能的结果
    注入与以往的开发思路
    abp的权限与导航菜单的关系
    ionic入坑记记录
    abp相关
  • 原文地址:https://www.cnblogs.com/lyfstorm/p/10116421.html
Copyright © 2011-2022 走看看