查找算法

查找的基本概念

  1. 静态查找表:对查找表经常要进行的两种操作是查询和检索
  2. 动态查找表:对查找表经常要进行的操作是插入和删除
  3. 关键字:是数据元素的某个数据项的值,用它来识别这个数据元素
  4. 主关键字:能唯一标识一个数据元素的关键字
  5. 次关键字:能标识多个数据元素的关键字
  6. 查找:根据给定的某个值,在查找表中确定是否存在一个其关键字等于给定值的记录或数据元素的过程称为查找
  7. 查找表:是指由同一类型的数据元素构成的集合
  8. 平均查找长度:其关键字和给定值进行过比较的记录个数的平均值(衡量查找算法的标准)
ASL = \sum_{i=1}^{n} {p_ic_i}
静态查找表
顺序查找
  • 顺序查找的基本思想:从表的一端开始,逐个进行记录的关键字和给定值的比较,若相等,则查找成功;若整个表均比较过,仍为找到,则查找失败。
  • 性能分析:
ASL_{SS}=\sum_{i=1}^{n} {p_ic_i}=\frac 1n \sum_{i=1}^{n} (n-i+1)=\frac {n+1}2
  • 时间复杂度:
f(n) = O(n)
  • 优点:算法简单且适应面广,对查找表的结构没有要求
  • 缺点:当n值较大时,查找效率较低
  • 运行代码
# 在列表中顺序查找特定数值x
def sequentialSearch(list, item):
    pos = 0  # 初始查找位置
    found = False  # 未找到数据
    while pos < len(list) and not found:  # 列表未结束并且还未找到则一直循环
        if list[pos] == item:  # 找到匹配对象,则返回True
            found = True
        else:  # 否则查找位置加1
            pos += 1
    return found


# 在列表中顺序查找最大值和最小值
def max(list):
    pos = 0
    iMax = list[0]
    while pos < len(list):
        if list[pos] > iMax:
            iMax = list[pos]
        else:
            pos += 1
    return iMax


def min(list):
    pos = 0
    iMin = list[0]
    while pos < len(list):
        if list[pos] < iMin:
            iMin = list[pos]
        else:
            pos += 1
    return iMin


def main():
    testlist = [1, 3, 33, 8, 37, 29, 32, 15, 5]
    print('测试列表:', testlist)
    print('3是否在列表中:', sequentialSearch(testlist, 3))
    print('13是否在列表中:', sequentialSearch(testlist, 13))
    print('列表最大值:', max(testlist))
    print('列表最小值:', min(testlist))


if __name__ == '__main__':
    main()
二分查找法
  • 二分查找法(折半查找)的基本思想:首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
  • 性能分析:
ASL_{SS}=\sum_{i=1}^{n} {p_ic_i}=\frac 1n \sum_{i=1}^{n} (j*2^{j-1})=\frac {n+1}n \log_2(n+1)-1

当n值较大时,

ASL_{SS} \approx  \log_2(n+1)-1
  • 时间复杂度:
f(n) = O(\log_2N)
  • 优点:比顺序查找效率高
  • 缺点:要求查找表进行顺序存储并且按关键字有序排列,且插入删除困难
  • 适用情况:适用于表不易变动,且又经常进行查找的情况
  • 运行代码
# 二分查找法的递归实现
def _binarySearch(key, list, low, high):
    if high <= low:  # 查找失败,返回-1
        return -1
    mid = (low + high) // 2  # 计算中间位置
    if list[mid] > key:  # 中间位置大于查找关键字,递归查找前一字表
        return _binarySearch(key, list, low, mid)
    elif list[mid] < key:  # 中间位置小于查找关键字,递归查找后一字表
        return _binarySearch(key, list, mid + 1, high)
    else:  # 中间位置等于查找关键字,查找成功,返回下标位置
        return mid


def binarySearch(key, list):
    if _binarySearch(key, list, 0, len(list)) == -1:
        print('关键字{}在列表中不存在'.format(key))
        return _binarySearch(key, list, 0, len(list))
    else:
        return _binarySearch(key, list, 0, len(list))


# 非递归实现二分查找法
def binarySearchNoRecursion(key, list):
    low = 0  # 左边界
    high = len(list) - 1  # 右边界
    while low <= high:
        mid = (low + high) // 2  # 计算中间位置
        if list[mid] > key:  # 中间位置大于查找关键,调整右边界,在前一字表查找
            high = mid - 1
        elif list[mid] < key:  # 中间位置小于查找关键,调整左边界,在后一字表查找
            low = mid + 1
        else:
            return mid
    return -1


def main():
    testlist = [1, 13, 26, 33, 45, 55, 68, 72, 83, 99]
    print("关键字33位于列表索引:", binarySearch(33, testlist))
    print("关键字58位于列表索引:", binarySearch(58, testlist))

    print("调用非递归函数:")
    print("关键字33位于列表索引:", binarySearchNoRecursion(33, testlist))
    print("关键字58位于列表索引:", binarySearchNoRecursion(58, testlist))


if __name__ == '__main__':
    main()
分块查找
  • 分块查找的主要思想:分块查找要求把一个大的线性表分解成若干块,每块中的节点可以任意存放,但块与块之间必须排序。假设是按关键码值非递减的,那么这种块与块之间必须满足已排序要求,实际上就是对于任意的i,第i块中的所有节点的关键码值都必须小于第i+1块中的所有节点的关键码值。此外,还要建立一个索引表,把每块中的最大关键码值作为索引表的关键码值,按块的顺序存放到一个辅助数组中,显然这个辅助数组是按关键码值费递减排序的。查找时,首先在索引表中进行查找,确定要找的节点所在的块。由于索引表是排序的,因此,对索引表的查找可以采用顺序查找或折半查找;然后,在相应的块中采用顺序查找,即可找到对应的节点。
  • 分块查找的优势:
  • 分块查找是折半查找和顺序查找的一种改进方法,折半查找虽然具有很好的性能,但其前提条件时线性表顺序存储而且按照关键码排序,这一前提条件在结点树很大且表元素动态变化时是难以满足的。而顺序查找可以解决表元素动态变化的要求,但查找效率很低。如果既要保持对线性表的查找具有较快的速度,又要能够满足表元素动态变化的要求,则可采用分块查找的方法。
  • 分块查找的速度虽然不如折半查找算法,但比顺序查找算法快得多,同时又不需要对全部节点进行排序。当节点很多且块数很大时,对索引表可以采用折半查找,这样能够进一步提高查找的速度。
  • 分块查找由于只要求索引表是有序的,对块内节点没有排序要求,因此特别适合于节点动态变化的情况。当增加或减少节以及节点的关键码改变时,只需将该节点调整到所在的块即可。在空间复杂性上,分块查找的主要代价是增加了一个辅助数组。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值