栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

数据结构与算法

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数据结构与算法

1. 搜索

搜索是在一个项目集合中找到一个特定项目的算法过程。搜索通常的答案是真的或假的,因为该项目是否存在。

搜索的几种常见方法:

  1. 顺序查找
  2. 二分法查找
  3. 二叉树查找
  4. 哈希查找
2. 二分法查找 (Binary Search) 2.1 二分法查找的优缺点

二分查找又称折半查找,优点是:

  • 比较次数少
  • ,查找速度快,
  • 平均性能好﹔

其缺点是:

  • 要求待查表为有序表
  • 插入删除困难

因此,折半查找方法适用于不经常变动而查找频繁的有序列表。

2.2 二分法查找的步骤

首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表希成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

2.3 二分法查找的例子

这里的二分法查找就和人查字典类似,首先翻到字典的一半位置( s t a r t + e n d 2 = 0 + 8 2 = 4 frac{start+end}{2} = frac{0+8}{2} = 4 2start+end​=20+8​=4),看看此时的字母是多少:

  • 如果我们要找的在该字母的前面,我们就将前面的对半分( s t a r t + e n d 2 = 0 + 3 2 = 1 frac{start+end}{2} = frac{0 + 3}{2} = 1 2start+end​=20+3​=1);
  • 如果我们要找的在该字母的后面,我们就将后面的对半分( s t a r t + e n d 2 = 2 + 3 2 = 2 frac{start+end}{2} = frac{2 + 3}{2} = 2 2start+end​=22+3​=2) -> 已经找到了!
  • 依次执行,直到找到目标位置…

在二分的时候一定要明确起始的index和结束的index!

看图说话:

2.4 二分法查找的注意事项

在二分的时候一定要明确起始的index和结束的index!

在二分的时候一定要明确起始的index和结束的index!

2.5 二分法查找的代码实现 2.5.1 递归实现
# coding: utf-8


def binary_search(alist, target):
    """二分法查找
        对于二分法查找的代码实现,最好的实现方式是使用递归
    """

    n = len(alist)

    if n > 0:  # 递归的终止条件
        mid = n // 2
        if alist[mid] == target:
            return True
        elif target < alist[mid]:  # 往左半部分查找
            return binary_search(alist[:mid], target)
        else:  # 往右半部分查找
            return binary_search(alist[mid + 1:],
                                 target)  # 注意alist[mid]这个数我们就不要了
    else:  # 说明没有该元素,则返回False
        return False


if __name__ == "__main__":
    ls = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print(ls)
    res_1 = binary_search(ls, target=26)
    res_2 = binary_search(ls, target=100)
    print(ls)
    print(res_1)
    print(res_2)
"""
    [54, 26, 93, 17, 77, 31, 44, 55, 20]
    [54, 26, 93, 17, 77, 31, 44, 55, 20]
    True
    False
"""
2.5.2 非递归的实现
# coding: utf-8


def binary_search(alist, target):
    """二分法查找(非递归版本)
        对于非递归版本,只能在原有list上进行操作,必须涉及:
            1. 起始位置下标
            2. 结束位置下标 
    """
    n = len(alist)
    first_idx = 0
    last_idx = n - 1

    while first_idx <= last_idx:  # 一旦first_idx > last_idx时,表示已经越界,没有该数值,返回False

        mid = (first_idx + last_idx) // 2

        if alist[mid] == target:
            return True
        elif alist[mid] > target:  # ←
            # 改变起始位置和结束位置就可以改变搜索范围
            last_idx = mid - 1  # 不要之前的mid了
        else:  # →
            first_idx = mid + 1  # 不要之前的mid了

    return False


if __name__ == "__main__":
    ls = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print(ls)
    res_1 = binary_search(ls, target=26)
    res_2 = binary_search(ls, target=100)
    print(ls)
    print(res_1)
    print(res_2)
"""
[54, 26, 93, 17, 77, 31, 44, 55, 20]
[54, 26, 93, 17, 77, 31, 44, 55, 20]
True
False
"""
2.5.3 非递归版本和递归版本的区别

两种代码实现之间最大的区别就在于:

  • 在写递归算法时,我们就用递归的思想去重新调用函数,但是使用递归在重新调用函数时会产生一个新的列表;
  • 在非递归的实现中,我们不能调用函数自身了,因此需要重新划分查找的范围
2.6 二分查找简洁代码 2.6.1 递归实现
def binary_search(alist, target):
    if len(alist) == 0:
        return False
    else:
        mid = len(alist) // 2
        if alist[mid] == target:
            return True
        elif alist[mid] > target:
            return binary_search(alist[:mid], target=target)  # [: mid-1]
        else:
            return binary_search(alist[mid + 1:], target=target) # [mid+1: n]
2.6.2 非递归实现
def binary_search(alist, target):
    first = 0
    last = len(alist) - 1
    while first <= last:
        mid = (first + last) // 2
        if alist[mid] == target:
            return True
        elif alist[mid] > target:
            last = mid - 1  # 此时first不变
        else:
            first = mid + 1  # 此时last不变
    return False
2.7 二分查找的时间复杂度

首先我们先考虑最坏时间复杂度。我们之前学习的希尔排序、归并排序和快排都会涉及到二分法,所以我们假设它一直需要二分,也就是 2 t = n 2^t = n 2t=n,即二分t次将整个数组分完,所以次数 t = log ⁡ 2 n t = log_2^n t=log2n​。

那么我们考虑一下它的最优时间复杂度,即第一次求得中间值就是目标值target,就不需要二分了,所以我们认为此时的时间复杂度为 O ( 1 ) O(1) O(1)。


  • 最优时间复杂度: O ( 1 ) O(1) O(1)
  • 最坏时间复杂度: O ( log ⁡ 2 n ) O(log_2^n) O(log2n​)

我们与之前从头到尾那样遍历去找元素的方法对比一下:

算法最优时间复杂度最坏时间复杂度最优说明
遍历 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)第一个元素即为target
二分查找 O ( 1 ) O(1) O(1) O ( log ⁡ 2 n ) O(log_2^n) O(log2n​)第一个中间的值就是target

很明显, O ( log ⁡ 2 n ) O(log_2^n) O(log2n​)的时间复杂度明显比 O ( n ) O(n) O(n)要低的多。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1001210.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号