算法-搜索

含义

搜索是指从元素集合找到某个特定元素的算法过程。

搜索过程通常返回True或False,分别表示元素是否存在。

有时,可以修改搜索过程,使其返回目标元素的位置。

 顺序搜索

"""
存储于列表等集合中的数据项彼此存在线性或顺序的关系,每个数据项的位置与其他数据项相关。
元素都是有序的,可以顺序访问,由此可以进行顺序搜索。

时间复杂度: O(n)
"""

# 无序列表的顺序搜索
def sequentialSearch(alist, item):
    pos = 0
    found = False
    
    while pos < len(alist) and not found:
        if alist[pos] == item:
            found = True
        else:
            pos += 1

    return found

# 有序列表的顺序搜索
def orderedSequentialSearch(alist, item):
    pos = 0
    found = False
    stop = False
    while pos < len(alist) and not found and not stop:
        if alist[pos] == item:
            found = True
        else:
            if alist[pos] > item:
                stop = True
            else:
                    pos += 1
    return found

二分搜索

# 有序列表的二分搜索(分治策略的例子)
# 分治:将问题分解成小问题,以某种方式解决小问题,然后整合结果,以解决最初的问题。
# 时间复杂度: O(logn)
def binarySearch(alist, item):
    first = 0
    last = len(alist) - 1
    found = False

    while first <= last and not found:
        midpoint = (first + last) // 2
        if alist[midpoint] == item:
            found = True
        else:
            if item < alist[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1
    return found

# 递归版本
def binarySearch2(alist, item):
    if len(alist) == 0:
        return False
    else:
        midpoint = len(alist) // 2
        if alist[midpoint] == item:
            return True
        else:
            if item < alist[midpoint]:
                return binarySearch2(alist[:midpoint], item)
            else:
                return binarySearch2(alist[midpoint+1:], item)

散列

散列函数一定要高效,以免它成为存储和搜索过程的负担。

如果散列函数过于复杂,引入的计算复杂度更高。

# 通过散列构建一个时间复杂度为O(1)的数据结构。
# 散列函数将散列表中的元素与其所属位置对应起来。
# 对散列表中的任一元素,散列函数返回一个介于0和m-1之间的整数。
# 散列函数可能存在冲突,也叫碰撞。显然,冲突给散列函数带来了问题。
# 给定一个元素集合,能将每个元素映射到不同的槽,这种散列函数称作完美散列函数。
# 构建完美散列函数的一个方法是增大散列表,使之能容纳每一个元素。
# 通常选择: 冲突数最少,计算方便,元素均匀分布于散列表中。

# 折叠法: 先将元素切成等长的部分(最后一部分的长度可能不同),然后将这些部分相加,得到散列值。
#     如436-555-4601,两位切割(43,65,55,46,01),相加得210,假设有11个槽,210除以11,得余数1,
#     即放到1号槽。也或者更进一步,在加总前每隔一个数反转一次。即(43,56,55,64,01)219%11=10。

# 平方取中法: 先将元素取平方,然后提取中间几位数。如果元素是44,先计算44的平方,然后提取中间两位,
#     44²=1936,中间两位93,然后取余,得5(93%11)。

# 为字符串构建简单的散列函数,针对异序词,下列散列函数得到值相同。此时可以引入字符位置作为权重因子。
# 即 c a t --->>> 99*1 + 97*2 + 116*3 = 641

def hash(astring, tablesize):
    sum = 0
    for pos in range(len(astring)):
        sum += ord(astring[pos])

    return sum%tablesize

# 处理冲突
# 当两元素被分配到同一个槽中时,必须通过一种系统化方法在散列表中安置第二个元素,即处理冲突。
# 如果散列函数是完美的,冲突就永远不会发生。然而,此前提往往不成立,因此处理冲突是散列计算重点。
# 解决方法:
#     一种方法:在散列表中找到另一个空槽,用于放置引起冲突的元素。简单做法是从初始散列开始,顺序
#             遍历散列表,直到找到一个空槽。此方法被称为开放定址法,它尝试在散列表中寻找下一个
#             空槽或地址。由于是逐个访问槽,因此也称作线性探测。
#             散列构建完成后,即可使用同样方法搜索元素。
#        缺点:线性探测会使元素出现聚集现象。即一个槽冲突太多,线性探测会填充满其附近槽。
#        为避免元素聚集,可扩展线性探测,不再依次查找,而是跳过一些槽,可使引起冲突的元素均匀。
#        rehash(pos) = (pos+skip)%sizeoftable。跨步大小需要保证所有槽都可被访问到,否则
#        会浪费槽资源。要保证这一点,通常设散列表大小为素数。
#        平方探测: 跨步大小是一系列完全平方数。
#    另一种方法: 链接法
#        让每个槽指向一个元素集合(或链表)的引用。即允许一个位置上有多个元素。发生冲突时,元素
#        仍然被插入其散列值对应的槽中。不过,如果同一个位置上的元素越来越多,搜索会越困难。
#        优点: 平均算来,每个槽元素不多,因此搜索可能更高效。
#
# 分析散列搜索算法:
#    最好情况下为O(1),即常数阶。但是,因为可能发生冲突,所以通常不会如此简单。
#    分析散列情况时,最重要的信息就是载荷因子λ。如果λ很小,那么发生冲突的概率就小。反之变大。
#    平均次数:
#        线性探测:0.5*[1+1/(1-λ)]
#        失败: 0.5*[1+1/(1-λ)²]
#        链接法: 1+λ/2
#        失败:    λ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值