Python数据结构与算法分析(第二版)学习笔记——搜索

39 阅读4分钟

搜索

搜索是从元素集合中找到某个特定元素的算法过程,结果通常是一个布尔值。

顺序搜索是一种最基本的搜索形式,他依次查找列表中的每个元素。要确定元素是否在列表中,就要依次将列表中每个元素进行比对,因此在最坏情况下需要比较列表的长度n次,最好的情况一开始就找到,比较一次,平均情况下比较n/2次。而在排序的情况下,效率将有所提高。

# 无序列表的搜索
def sequentialsearch(alist,item):
    pos = 0
    found = False

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

二分查找,在列表有序情况下,可以从中间的元素开始考虑,这样每次就能排除一般的元素。二分查找的时间复杂度是logn。但在n较少时,排序引起的额外开支可能是不必要的。

def binarysearch(alist,item):
    first = 0
    last = len(alist) - 1
    found = False

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

    return found
# 二分查找的递归

def binarysearchpro(alist,item):
    if len(alist) == 0:
        return False
    else:
        midpoint = len(alist) // 2
        if item == alist[midpoint]:
            return True
        else:
            if item < alist[midpoint]:
                return binarysearchpro(alist[:midpoint],item)
            else:
                return binarysearchpro(alist[midpoint+1:],item)

散列表是一个元素集合,他的每个位置是一个可以存储元素的槽,槽用一个0开始的元素标记,以下是一个有11个槽的散列表。

image.png

散列函数将散列表元素与其位置对应起来,对散列表元素,散列表函数返回0到m-1的元素

使用余数做散列值,而散列的占用率称载荷因子。

image.png

在搜索目标时,仅需要计算出槽编号并查看对应值。不过他需要元素间的散列值不同。完美散列函数指的是每个元素能映射不同的槽。扩展元素的方法折叠法、平方取中法。 折叠法是将元素平均切分求和,再计算其元素。平方取中法,再从中间取几位。

处理冲突

开放定址法法+线性探查:发生冲突后遍历散列寻找空的地址存放元素。再散列是发生冲突后寻找另一个槽的过程,同时可以通过跨步大小使得元素的分布更加平均。平方探测就是在限定探测的基础上,用平方扩展跨步的大小。

链接法,在同一个位置存储多个元素。

image.png

字典可以利用键来查找对应的值。

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] == None:
            self.slots[hashvalue] = key
            self.data[hashvalue] = data
        else:
            if self.slots[hashvalue] == key:
                self.data[hashvalue] = data #替换

            else:
                nextslots =  self.rehash(hashvalue,len(self.slots))
                while self.slots[nextslots] != None and  self.slots[nextslots] != key:
                    nextslots = self.rehash(nextslots,len(self.slots))
                if self.slots[nextslots] == None:
                    self.slots[nextslots] = key
                    self.data[nextslots] = data
                else:
                    self.data[nextslots] = data


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

用字典实现,字典可以用栈来查找,这种关系被称为映射。这个散列表的初始大小为11,在处理冲突时采用+1的散列函数的线性探测法。在put方法中,先计算哈希值用来做寻找槽的散列值,如果槽不存在键,就存入键和值,如果键存在且相同,就用新的值代替旧的值。除此以外,继续寻找一个槽来重复以上过程,新的散列值为nextslot。

而在get方法中,如果查找值不在初始散列值对应的槽中,就寻找下一个值,直到再次循环到初始散列值时,若仍未找到则停止程序。

class Hashtable:
    def __init__(self):
        self.size = 11
        self.slot = [None] * self.size
        self.data = [None] * self.size


    def put(self,key,data):
        hashvalue = self.hashfunction(key,len(self.slot))
        if self.slot[hashvalue] == None:
            self.slot[hashvalue] = key
            self.data[hashvalue] = data
        else:
            if self.slot[hashvalue] == key:
                self.data[hashvalue] = data
            else:
                nextslot = self.reshash(hashvalue,len(self.slot))
                while self.slot[nextslot] != None and self.slot[nextslot] != key:
                    nextslot = self.reshash(hashvalue, len(self.slot))

                if self.slot[nextslot] == None:
                    self.slot[nextslot] = key
                    self.data[nextslot] = data
                else:
                    self.data[nextslot] = data


    def get(self,key):
        startslot = self.hashfunction(key,len(self.slot))
        found = False
        stop = False
        data = None
        pos = startslot
        while self.slot[pos] != None and not stop and not found:

            if self.slot[pos] == key:
                found = True
                data = self.data[pos]
            else:
                pos = self.reshash(pos,len(self.slot))
                if pos == startslot:
                    stop = True
        return data
    def hashfunction(self,key,size):
        return key % size
    def reshash(self,oldhash,size):
        return (oldhash + 1) % size
    def __getitem__(self, key):
        return self.get(key)
    def __setitem__(self, key, data):
        self.put(key,data)