算法之顺序、二分、hash查找


一、查找/搜索

  • 定义:搜索是在元素集合中查找特定元素的算法过程。搜索通常返回元素是否存在(布尔值),有时也返回元素的位置。
  • Python 中的简单搜索:使用 in 运算符可以快速判断元素是否在列表中。
>>> 15 in [3, 5, 2, 4, 1]
False
>>> 3 in [3, 5, 2, 4, 1]
True

二、顺序查找

原理

  • 从列表的第一个元素开始,依次比较每个元素,直到找到目标元素或遍历完整个列表。
  • 如果列表是无序的,顺序查找是唯一的选择。

代码实现

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

testlist = [1, 2, 32, 8, 17, 19, 42, 13, 0]
print(sequentialSearch(testlist, 3))  # False
print(sequentialSearch(testlist, 13))  # True

分析

  • 时间复杂度:O(n)
    • 最好情况:O(1)(目标元素在列表开头)。
    • 最坏情况:O(n)(目标元素在列表末尾或不存在)。
    • 平均情况:O(n/2) ≈ O(n)。

三、有序列表的顺序查找

原理

  • 如果列表是有序的,可以在查找过程中提前终止。如果当前元素大于目标元素,则目标元素不存在。

代码实现

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

testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42]
print(orderedSequentialSearch(testlist, 3))  # False
print(orderedSequentialSearch(testlist, 13))  # True

分析

  • 时间复杂度:O(n)
    • 最好情况:O(1)(目标元素在列表开头或目标元素小于列表中所有元素)。
    • 最坏情况:O(n)(目标元素在列表末尾)。
    • 平均情况:O(n/2) ≈ O(n)。

四、二分查找

原理

  • 利用列表的有序性,每次比较中间元素,根据目标值与中间值的大小关系,选择左半部分或右半部分继续查找。
  • 每次比较后,搜索范围减半。

代码实现

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

testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42]
print(binarySearch(testlist, 3))  # False
print(binarySearch(testlist, 13))  # True

分析

  • 时间复杂度:O(log n)
    • 每次比较后,搜索范围减半,因此最多需要 log₂(n) 次比较。

五、哈希查找

原理

  • 利用哈希函数将元素映射到哈希表的某个位置,从而实现快速查找。
  • 哈希函数需要满足:
    • 快速计算。
    • 尽量减少冲突(多个元素映射到同一个位置)。

哈希函数

  • 简单余数法h(item) = item % table_size
  • 分组求和法:将元素的数字分组求和后取余。
  • 平方取中法:先平方,再取中间几位数字,最后取余。

冲突解决

  • 开放寻址法:从冲突位置开始,顺序查找下一个空槽。
  • 线性探测:如果槽 i 被占用,则尝试槽 i+1,直到找到空槽。

代码实现

class HashTable:
    def __init__(self):
        self.size = 11  # 哈希表大小,通常选择质数
        self.slots = [None] * self.size  # 存储键
        self.data = [None] * self.size  # 存储值

    def put(self, key, data):
        hashvalue = self.hashfunction(key, len(self.slots))
        if self.slots[hashvalue] is None:
            self.slots[hashvalue] = key
            self.data[hashvalue] = data
        else:
            if self.slots[hashvalue] == key:
                self.data[hashvalue] = data  # 替换旧值
            else:
                nextslot = self.rehash(hashvalue, len(self.slots))
                while self.slots[nextslot] is not None and self.slots[nextslot] != key:
                    nextslot = self.rehash(nextslot, len(self.slots))
                if self.slots[nextslot] is None:
                    self.slots[nextslot] = key
                    self.data[nextslot] = data
                else:
                    self.data[nextslot] = data  # 替换旧值

    def get(self, key):
        startslot = self.hashfunction(key, len(self.slots))
        data = None
        stop = False
        found = False
        position = startslot
        while self.slots[position] is not None and not found and not stop:
            if self.slots[position] == key:
                found = True
                data = self.data[position]
            else:
                position = self.rehash(position, len(self.slots))
                if position == startslot:
                    stop = True
        return data

    def hashfunction(self, key, size):
        return key % size

    def rehash(self, oldhash, size):
        return (oldhash + 1) % size

    def __getitem__(self, key):
        return self.get(key)

    def __setitem__(self, key, data):
        self.put(key, data)

使用示例

H = HashTable()
H[54] = "cat"
H[26] = "dog"
H[93] = "lion"
H[17] = "tiger"
H[77] = "bird"
H[31] = "cow"
H[44] = "goat"
H[55] = "pig"
H[20] = "chicken"

print(H.slots)
print(H.data)

print(H[20])  # 输出: chicken
print(H[17])  # 输出: tiger
H[20] = "duck"
print(H[20])  # 输出: duck
print(H[99])  # 输出: None

分析

  • 时间复杂度:O(1)(理想情况下)
    • 如果哈希函数设计良好且冲突较少,查找操作可以在常数时间内完成。
  • 冲突问题:冲突可能导致查找时间增加,但通过合理设计哈希函数和冲突解决机制,可以尽量减少冲突。

posted @ 2021-07-13 15:22  杨梅杨梅  阅读(223)  评论(0)    收藏  举报