检索算法又叫查找算法,可以分为排序检索和非排序检索两大类。排序的数据为高校检索提供了基础。当然非排序也存在,只不过效率很低。本章一起来学习几种检索算法。
线性查找(Linear Search)又叫作顺序查找,是最简单的一种查找方法。
图所示为线性查找过程,从左到右依次比较查找。查找值key=5时,
返回没有找到;
def linearfind(arr, key):n = len(arr)i = 0while i < n:if arr[i] == key:return ii += 1return -1s1 = [66, 79, 65, 25, 54, 99, 101, 100]
key = 6
r = linearfind(s1, key)
if r != -1:print(f'{key}被找到,在第{r}位上')
else:print(f"{key}没有被找到")
运行结果
二分查找( Binary Search)算法采用折半查找思路,实现对有序元素的快速查找。
图示为二分查找过程,列表共有n=11个元素。
第一次,取中间值mi-1/2-5,其左边范围为left=0、 右边范围为right=10, 比较结果
key-55在右半边的范围内。
第二次,取新的中间值mid-s/2 2,其左边范围为lef-5+1、右边范围为igh=10,比较结果
key-55在右半边的范围内。
第三次,取新的中间值m-2/2=I,其左边范围为let-6+2+1.右边范围为rgh=l0,比较结
果在下标10处找到55。
def binaryrearch(arr, key):left = 0right = len(arr)while left <= right:mid = (right - left) // 2 # 二分法中间位置if arr[left + mid] < key: # key 在右半范围left = left + mid + 1elif arr[left + mid] > key: # 左半范围left = left + mid - 1else:return left + mid # 找到下标 return -1s1 = [0, 10, 20, 30, 40, 50, 60, 70, 80]
print(f"查找列表{s1}")
key = 40
r = binaryrearch(s1, key)
if r == -1:print(f"未找到{key}元素")
else:print(f"在列表第{r}找到{key}元素")
运行结果
插值查找(Interpolation Search) 是基于有序数列的元素查找,在采纳二分查找算法的思路基础上进行了改进。在最小值、最大值范围内,用公式确定中间分割比较点mid
图所示为插值查找过程,left=0, right=8, mid=8*(50-0)/50=8。
def valuyesearch(arr, key):left = 0right = len(arr) - 1while left < right:mid = left + int((right - left) * (key - arr[left]) / (arr[right] - arr[left]))if key < arr[mid]:right = mid - 1elif key > arr[mid]:left = mid + 1else:return midreturn -1s1 = [0, 10, 20, 30, 40, 50, 60, 70, 80]
print(f"查找列表{s1}")
key = 60
r = valuyesearch(s1, key)
if r == -1:print(f"未找到{key}元素")
else:print(f"在列表第{r}找到{key}元素")
运行结果
斐波那契数列(Fibonacci Sequence)又称黄金分割数列,指的是这样一个数列: 1、 1、 2、3、5、8、13、21、······ 在数学上斐波那契被递归方法如下定义: F(1)=1, F(2)=1, F(n)=F(n 1)+F(n-2) (n≥2)。该数列越往后,相邻的两个数的比值越趋向于黄金比例值(0.618)。
斐波那契查找就是在二分查找的基础上根据斐波那契数列对数据进行分割的。
图示为斐波那契查找过程。
第一步,判断s数列的长度是否满足n=13,如果不满足,则用最后一个元素100 补充3个 100 (13-n=3)。
第二步,中间下标值mid取最右边值13-1 (斐波那契最后一个数是13), 因为70<100,说明70值范围在左边。
第三步,中间下标值mid取8-1 (斐波那契倒数第2个数是8),key=70, 找到元素。
def fibonaccisearch(arr,key):n = len(arr)i = 1F = [1,1,]while F[i] <= n:F.append(F[i-1]+F[i])i += 1print(f'生成最右边的斐波那契数为{F}')left = 0right = n - 1f = F[i]print(f'生成的最右边的斐波那契数为{f}')i = 0while i< f - n:arr.append(arr[right])i += 1print(arr)k = len(F)t = 0while left <= right:t += 1if k < 2:mid = leftelse:mid = left + F[k-1] - 1print(f"left={left},mid={mid},right={right}")if key < arr[mid]:right = mid - 1k -= 1elif key > arr[mid]:left = mid + 1k -= 2else:print(f"查找次数{t}")if mid <= right:return midelse:return rightprint(f"查找次数{t}")return False
key = 70
s1 = [0,3,5,7,10,30,50,80,100]
r = fibonaccisearch(s1,key)
if r == False:print(f"{key}没有在列表中找到")
else:print(f"查找{key}在列表里的下标{r}")
运行结果
分块查找,又叫索引顺序查找,是顺序查找的一种改进方法。
def linearfind(arr, key):n = len(arr)i = 0while i < n:if arr[i] == key:return ii += 1return -1def blocksearch(arr, key, index, n):b_max = -1j = 0for i in index:j += 1if key <= i:b_max = ibreakif b_max == -1:return -1print(f"块内最大值为{b_max}")r = linearfind(arr[(j - 1) * n:(j * n - 1)], key)print(f"线性查找结果:{r}")if r == -1:return -1else:return (j - 1) * n + rs1 = [1, 4, 2, 8, 3, 10, 89, 20, 19, 50, 200, 128, 121, 120, 110]
index = [8, 89, 200]
key = 2
r = blocksearch(s1, key, index, 5)
if r == -1:print(f"{key}在列表里不存在")
else:print(f"{key}在列表里的下标为{r}")
运行结果
哈希查找算法采用键值对存储数据的哈希表,通过哈希函数决定存储地址,并利用元素对应的地址直接在哈希表里进行1次访问即解决查找问题,时间复杂程度为O(1),效率很高。
第一步,建立哈希表,哈希表的元素为键值对,在python语言里可以看作字典。键用于存放唯一性地址,值用于存放数据。
第二步,用哈希函数(hash()产生键(address)),产生键的唯一要求是产生具有唯一地址的数值。产生键的算法有很多种,这里采用了除留余数法,公式为元素值%n,n为哈希表的长度;对于重复发生冲突的地址,采用试探下一个地址的方法解决(观察48产生的地址9)
第三步,查找元素时,也采用哈希函数算法先找到地址,然后在哈希表里1次找到对应的值(如果找不到,试探下一个地址,直到哈希表结束)。
maxsize = 20
hashtable = {i: None for i in range(maxsize)}
print(f"哈希表大小为{len(hashtable)}")def hash(value):address = value % maxsizeif hashtable[address] == None:return addresselse:while address < maxsize:address += 1if hashtable[address] == None:return addressreturn -1def buildhashtable(values):for value in values:address = hash(value)print(f"建立的键值对({address},{value})")if address != -1:hashtable[address] = valueelse:print(f"数值{value}存放的哈希表地址不够")return hashtabledef findvalue(hasht, value):address = value % maxsizeif hasht[address] == value:return Trueelse:address += 1while address < maxsize:if hasht[address] == value:return Trueaddress += 1return Falses1 = [20, 30, 1, 2, 5, 8, 23, 44, 48]
value = 44
if findvalue(buildhashtable(s1), value):print(f"在哈希表里找到{value}")
else:print(f"在哈希表里找不到{value}")
运行结果
回溯算法(Backtracking Algorithm)是一个类 似枚举的搜索尝试过程,在尝试过程中寻找问题的解,当发现条件不满足要求时,回退寻找另外路径求解,反复迭代上述过程,直到找出满足要求的所有条件(即为一个解),或者不存在解。
许多复杂的、规模较大的问题都可以使用回溯查找法求解,该算法有“通用解题方法”的美称。
图示为走迷宫主要思路。左上角第一个矩阵中0代表墙,1代表道路。要求只能从第1列的任意一个口进入,最后一列任意一个口出去, 才算走出迷宫。图右上角是一.条能走通的迷宫路。
def findnext(m, record, r, c):n = len(m)if (c + 1 < n):if record[r][c + 1] == 0:if m[r][c + 1] == 1:record[r][c + 1] = 1return r, c + 1else:b_r, b_c = r, c + 1if r + 1 < n:if record[r + 1][c] == 0:if m[r + 1][c] == 1:record[r + 1][c] = 1return r + 1, celse:b_r, b_c = r + 1, cif r - 1 >= 0:if record[r - 1][c] == 0:if m[r - 1][c] == 1:record[r - 1][c] = 1return r - 1, celse:b_r, b_c = r - 1, cif c - 1 >= 0:if record[r][c - 1] == 0:if m[r][c - 1] == 1:record[r][c - 1] = 1return r, c - 1else:b_r, b_c = r, c - 1m[r][c] = 0if r + 1 == n and m[r][0] == 0:print(r, c)b_r, b_c = r + 1, 0return b_r, b_cdef solution_maze(m, record):row, col = 0, 0n = len(m)i = 0while i < n:if m[i][0] == 1:row = irecord[i][0] = 1breaki += 1if i == n - 1:print("第一列无入门地方,终止执行")while i < n:if (row < n and row >= 0) and (col < n and col >= 0):if col == n - 1:for one in record:print(one)return 1row, col = findnext(m, record, row, col)if col == 0:i += 1row = ireturn 0if __name__ == '__main__':maze = [[0, 1, 0, 1, 1, 1, 1, 1, 0],[1, 1, 0, 1, 0, 1, 0, 1, 1],[0, 1, 1, 1, 0, 1, 0, 0, 0],[0, 1, 0, 0, 1, 1, 0, 0, 0],[0, 1, 1, 1, 1, 0, 1, 1, 1],[0, 0, 1, 0, 0, 0, 1, 0, 1],[1, 1, 1, 1, 1, 1, 1, 0, 1],[0, 1, 1, 0, 0, 0, 0, 0, 1],[1, 0, 1, 0, 0, 0, 0, 0, 1]]n = len(maze)recordpath = [([0]*n) for _ in range(n)]solution_maze(maze,recordpath)
运行结果
有关检索算法就这些了,有哪块不懂得欢迎 点击这里加入直接问问题,一起探讨。