数组中的元素查找
顺序查找
顺序查找
顺序查找思路比较简单,就是遍历,有就返回,所以数组有序无序无所谓,最优时间复杂度为O(1),最坏为O(n)。
# 如果只找到第一个位置
def sequentail_search_first(lis,target):
for ii in range(len(lis)):
if lis[ii]==target:
return ii
return -1
# 想要找到所有的位置
def sequentail_search(lis,target):
pos=[]
for ii in range(len(lis)):
if lis[ii]==target:
pos.append(ii)
if len(pos)==0:
return -1
else:
return pos
分块查找(基于顺序查找改进版本)
先把数组分成k个部分,找每一部分的最大值,记录下每一个模块第一个数的索引,比较目标值与每一个模块最大值的大小,如果比模块最大值小就有可能在当前模块,锁定分组,在分组中执行顺序查找。
# 如果只返回第一个找到的数值
def block_search_first(lis,target,block_len=10):
index_value={}
for ii in range(len(lis)):
if ii%block_len==0:
index_value[ii]=max(lis[ii:ii+block_len])
for key,value in index_value.items():
if target<=value:
out=sequentail_search_first(lis[key:key+block_len],target)
if out!=-1:
return out
return -1
# 如果要返回所哦有值
def block_search(lis,target,block_len=10):
index_max={}
for ii in range(len(lis)):
if ii%block_len==0:
index_max[ii]=max(lis[ii:ii+block_len])
pos=[]
for key,value in index_max.items():
if value>=target:
res=sequentail_search(lis[key:key+block_len],target)
if res!=-1:
pos.extend([key+jj for jj in res])
if len(pos)==0:
return -1
else:
return pos
二分法查找
有序数组二分查找
实际上就是猜数字,每次都猜中间的数字,第一轮猜最中间的,如果大了,就在右边找,如果小了就在左边找,代码上只要更新左右的位置和中间的位置,直到找到为止。要求数组比如是有序的。
def binary_search(lis,target):
head=0
tail=len(lis)-1
while head<=tail:
mid=(head+tail)//2
if lis[mid]==target:
return mid
elif lis[mid]>target:
tail=mid-1
else:
head=mid+1
return -1
平均时间复杂度为O(logn)
插值查找
是二分法的改进版本,实际上就是每次猜数不猜中间的,而是猜尽可能离目标值近的,咋做到的呢,每次猜数的位置=head+(tail-head)*((target-lis[head])/(lis[tail]-lis[head]))。
def binary_search(lis,target):
head=0
tail=len(lis)-1
while head<=tail:
mid=head+int((tail-head)*((target-lis[head])/(lis[tail]-lis[head])))
if lis[mid]==target:
return mid
elif lis[mid]>target:
tail=mid-1
else:
head=mid+1
return -1
查找成功或者失败的时间复杂度均为O(log(logn))
斐波那契查找
也是二分法的改进版本,改mid的位置。以下链接讲得很清楚。大概的思想是,先构建一个斐波那契数列,然后找刚好>=lis的长度的最小值,比如lis长度是25,那么最近的斐波那契数列是34,则用lis的最后一个数值填充lis长度到34,34的前两个数是13和21,那么先猜head+13-1为mid,看与target的大小,之后重复该过程。
斐波那契查找
哈希查找
复杂度为O(1),就是把要找的数变成dict的key,索引变成dict的value,以便于很快的找到数对应的位置。随便在leetcode上找了个case-猜数字游戏,题目如下:
你正在和你的朋友玩 猜数字(Bulls and Cows)游戏:你写下一个数字让你的朋友猜。每次他猜测后,你给他一个提示,告诉他有多少位数字和确切位置都猜对了(称为“Bulls”, 公牛),有多少位数字猜对了但是位置不对(称为“Cows”, 奶牛)。你的朋友将会根据提示继续猜,直到猜出秘密数字。
请写出一个根据秘密数字和朋友的猜测数返回提示的函数,用 A 表示公牛,用 B 表示奶牛。
请注意秘密数字和朋友的猜测数都可能含有重复数字。示例 1:
输入: secret = “1807”, guess = “7810”
输出: “1A3B”
解释: 1 公牛和 3 奶牛。公牛是 8,奶牛是 0, 1 和 7。
示例 2:
输入: secret = “1123”, guess = “0111”
输出: “1A1B”
解释: 朋友猜测数中的第一个 1 是公牛,第二个或第三个 1 可被视为奶牛。
这个题目返回的A为匹配的,B为不匹配但是有包含的,前面的数为A和B的数目,比如1A3B就是1个匹配的3个不匹配但是包含的。
思路是这样:先创建了一个dict,里面key为每一位数字,value为每个数字对应可能的位置的列表。如果发现A就计数,然后要删掉这个位置。以上操作结束后,如果发现B,就计数并且删掉对应数字的一个位置即可。
class Solution(object):
def getHint(self, secret, guess):
"""
:type secret: str
:type guess: str
:rtype: str
"""
dict_secret={}
set_secret=set(list(str(secret)))
for ii in set_secret:
dict_secret[ii]=[]
for jj in range(len(str(secret))):
dict_secret[str(secret)[jj]].append(jj)
A=0
B=0
for ii in range(len(str(secret))):
if str(secret)[ii]==str(guess)[ii]:
A+=1
dict_secret[str(secret)[ii]].remove(ii)
for ii in range(len(str(secret))):
if str(secret)[ii]!=str(guess)[ii] and str(guess)[ii] in str(secret):
if len(dict_secret[str(guess)[ii]])!=0:
B+=1
dict_secret[str(guess)[ii]].pop(0)
return str(A)+"A"+str(B)+"B"
结果不是很好,只是简单的写一下怎么用到哈希表的。
基本图形搜索算法
广度优先搜索(BFS)
用来解决最短路径问题的查找算法。比如下图是一个有向图的问题,如何用BFS找到两点之间的最短路径。
A只能到B,C,D;B只能到E,C只能到E,F以此类推,先想办法描述图,咋描述呢,如下:
graph={}
graph["A"]=["B","C","D"]
graph["B"]=["E"]
graph["C"]=["E","F"]
graph["D"]=["G"]
graph["E"]=[]
graph["F"]=["G"]
graph["G"]=[]
然后我们先来解决第一个问题,能否到达的问题,比如A点能否到达G点。
def BFS1(graph,start,end):
queue=[start]
while queue:
current_point=queue.pop(0) # 返回当前所在点,并从队列里面删掉
for next_point in graph[current_point]: #遍历当前点可到达的点
if next_point==end:
return True
else: # 如果当前点不可到达目的地,那就全部收进队列,并且一个一个找他们的下一个点能不能到达目的地
queue.append(next_point)
return False
上面的代码其实很容易读懂,那现在如果要知道能到达的路径都有啥,最短是啥?咋整?那其实就是记录下走过的点,BFS算法可以保证找到的第一个路径为最短路径。
def BFS2(graph,start,end):
queue=[(start,start)] ## 多了一个start记录path
while queue:
(current_point,path)=queue.pop(0)
for next_point in graph[current_point]:
if next_point==end:
return "最短步数为"+str(len(path.replace("->","")))+" 最短路径为"+ path+"->"+next_point
else:
queue.append((next_point,path+"->"+next_point))
return "无法到达目的地"
深度优先搜索
与广度优先搜索的区别是优先死磕一个路径一直走到最深(BFS是一层一层的找),可以保证最先找到最远的路径。只需要把队列改成栈即可。
def DFS(graph, start, end):
stack = [(start, [start])]
while stack:
(current_point, path) = stack.pop()
for next_point in set(graph[current_point]) - set(path):
if next_point == end:
return path + [next_point]
else:
stack.append((next_point, path + [next_point]))