数据结构与算法Python版 07 算法之搜索篇

线性搜索

顺序查找也被称之为线性查找,因在Python中list的底层为顺序表存储,故我们可以使用index来循环遍历整个列表获得所需要查找的数据项。

它的时间复杂度为O(n),如果刚好被搜索的数据项排列在最后一个,那么整个查找是非常耗费时间的:

image-20210618153251927

如下所示,如果查找数据项50,将会查找11次:

def seqLinerSearch(seq, findItem):
    idx = 0
    count = 0
    seqLenght = len(seq)
    while idx < seqLenght:
        count += 1
        if findItem == seq[idx]:
            return findItem, count
        idx += 1
    return None, count


if __name__ == "__main__":
    lst = [18, 22, 34, 2, 71, 8, 92, 37, 82, 91, 50]
    result, count = seqLinerSearch(lst, findItem=50)
    print(result, count)

# 50 11

有序的必要性

现在,让我们观察上面的代码是否有什么可以优化的地方。

我们的查找在数据项不存在的情况下总是会遍历完整个列表,导致这个问题的主要原因还是因为传入的列表是无序的。

如上述代码,我们查找数据项53,它总是一遍一遍的进行对比,直至对比完成整个列表发现都没有符合条件的数据项后才会返回,时间复杂度总是为O(n),所以要针对这个情况进行简单的优化。

如果我们将列表排为有序的状态,当查找到71后,也就发现了没有必要往后查找,故至此直接返回即可。

image-20210618154315988

优化策略:

  • 列表要有序
  • 如果正在遍历的数据项大于被查找的数据项,则代表此列表中没有该数据项

代码实现如下,虽然整体的时间复杂度还是O(n),但是已经做了很大的优化了:

def seqLinerSearch(seq, findItem):
    idx = 0
    count = 0
    seqLenght = len(seq)
    while idx < seqLenght and seq[idx] <= findItem:
        count += 1
        if findItem == seq[idx]:
            return findItem, count
        idx += 1
    return None, count


if __name__ == "__main__":
    lst = [2, 8, 18, 22, 34, 37, 50, 71, 82, 91, 92]
    result, count = seqLinerSearch(lst, findItem=53)
    print(result, count)

# None 7

注意:排序本身就花费时间,复杂度为O(n log n)

二分搜索

通过上面的2个例子,可以得出一个结论:

对查找来说,保持数据项的有序是十分必要的

那么现在基于这个先决条件,介绍一种查找效率更高的算法,二分查找法。

二分查找的实现理念:每次只查找列表中间元素的值,判定这个中间元素与被查找元素的关系,通过关系将列表进行折半后,继续查找,时间复杂度是O(logn),如下所示二分查找法与顺序查找的对比图:

史上最简单的二分查找讲解- 知乎

实现二分查找,查找数据项50仅需要3次:

#! /usr/local/bin/python3
# -*- coding:utf-8 -*-

def binarySearch(seq, findItem):
    count = 0
    l_idx = 0
    r_idx = len(seq) - 1

    while l_idx <= r_idx:
        count += 1

        m_idx = (l_idx + r_idx) // 2

        # 如果查找的元素等于当前列表中部索引的元素,则返回
        if findItem == seq[m_idx]:
            return findItem, count

        # 如果查找的元素大于当前列表中部索引的元素,则重新定义左侧索引
        elif findItem > seq[m_idx]:
            l_idx = m_idx + 1

        # 如果查找的元素小于当前列表中部索引的元素,则重新定义右侧索引
        else:
            r_idx = m_idx - 1

    else:
        return None, count


if __name__ == '__main__':
    result, count = binarySearch([2, 8, 18, 22, 34, 37, 50, 71, 82, 91, 92], 50)
    print(result, count)

# 50 3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值