总述 在给定的集合、多重集(允许多个元素具有相同的值)中找给定值
(查找键,search key )
顺序搜索 
折半查找:效率高但应用受限 
将原集合用另一种形式表示以方便查找 
 
评价 没有任何一种查找算法在任何情况下都是最优的
有些算法速度快,但是需要较多存储空间 
有些算法速度快,但是只适合有序数组 
 
查找算法没有稳定性问题,但会发生其他问题
无序线性表查找 顺序查找 算法 
将给定列表中连续元素和给定元素查找键进行比较
直到遇到匹配元素:成功查找 
匹配之前遍历完整个列表:失败查找 
 
 
 
1 2 3 4 5 6 7 8 9 10 11 12 SequentialSearch(A[0. .n-1 ], K) 	 	 	 	A[n] = K 	i = 0  	while  A[i] != K do  		i = i + 1  	if  i < n 		return  i 	else  		return  -1  
 
改进 
将查找键添加找列表末尾,查找一定会成功,循环时将不必每次
检查是否到列表末尾 
如果给定数组有序:遇到等于(查找成功)、大于(查找失败)
查找键元素,算法即可停止 
 
二叉查找树 算法 
特点 
预排序查找 对线性表预排序,有序表中查找速度快得多
算法 1 2 3 4 5 6 7 PreorderSearch(A[0. .n-1 ]) 	 	 	 	对B[(数组元素, 索引)]进行预排序 	使用折半查找寻找二元组 	返回二元组中索引 
 
特点 
预排序检验唯一性 1 2 3 4 5 6 7 8 9 PresortElementUniqueness(A[0. .n-1 ]) 	 	 	 	对数组排序 	for  i=0  to n-2  do  		if  A[i] = A[i+1 ] 			return  false  	return  true  
 
预排序求众数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PresortMode(A[0. .n-1 ]) 	 	 	 	对数组A排序 	i = 0  	modefrequency = 0  	while  i <= n-1  do  		runlength = 1  		runvalue = A[i] 		while  i + runlength <= n-1  and  A[i+runlength] == runvalue 			 			runlength = runlength+1  		if  runlength > modefrequency 			modefrequency = runlength 			modevalue = runvalue 		i = i+runlength 	return  modevalue 
 
有序顺序表查找 
折半查找/二分查找 二分查找:利用数组的有序性,通过对查找区间中间元素进行判断,
缩小查找区间至一般大小
基本版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 BinarySearchEndReturnV1(nums[0. .n-1 ], target): 	// 非递归折半查找基本版,不直接返回,方便嵌入 	// 输入:升序数组nums[0. .n-1 ]、查找键target 	// 输出:存在返回数组元素下标m,否则返回-1  	left, right = 0 , n-1  	while  left <= right: 		mid = (left + right) // 2  		if  nums[mid] == target: 			break  		elif  nums[mid] < target: 			left = mid+1  		else : 			right = mid-1  	else : 		assert (mid == right == left-1 ) 	 	if  nums[mid] == target: 		return  mid 	else : 		return  -1  
 
输入判断:无需判断输入列表是否为空
 
初始条件:left, right = 0, n-1
左、右均未检查、需检查 
即代码中查找区间为闭区间 $[left, right]$,此时
搜索区间非空 
 
 
中点选择:mid = (left + right) // 2
向下取整 
对此版本无影响,left、right总是移动,不会死循环 
 
 
循环条件:left <= right
搜索区间为闭区间,则相应检查条件为left <= right,
否则有元素未被检查 
在循环内left、right可能重合 
 
 
更新方式:left=mid+1, right=mid-1
 
终止情况:right=left-1、mid=left/right
循环条件、循环内部+1保证:上轮循环left==right 
越界终止情况:左、右指针均剔除mid,两侧均可能越界
mid=right=n-1, left=n 
mid=left=0, right=-1 
 
 
 
 
target位置
若存在target:必然在mid处,循环结束只需判断mid 
若不存在target
target位于[right, left]间 
left=mid+1=right+1表示小于target元素数量 
 
 
 
 
 
需要和左右邻居(查找结束后) 、left、right比较
  情况下,容易考虑失误,不适合扩展使用 
 
 
高级版1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 BinarySearchV2(nums[0. .n-1 ], target): 	left, right = 0 , n 	while  left < right: 		mid = (left + right)  		if  nums[mid] == target: 			break  		elif nums[mid] < target: 			left = mid+1  		else : 			right = mid 	else : 		assert(mid-1  == left == right) 	if  nums[mid] == target: 		return  mid 	else : 		return  -1  
 
输入判断:无需判断输入列表是否为空
 
初始条件:left, right = 0, n
左未检查、需检查,右无需检查 
即代码中查找区间为左闭右开区间 $[left, right)$ 
 
 
中点选择:mid = (left + right) // 2
向下取整 
此处必须选择向下取整,否则left=right-1时进入死循环 
即:循环内检查所有元素,取整一侧指针必须移动  
 
 
循环条件:left < right
搜索区间为左闭右开区间,则检查条件为left < right,
此时搜索区间非空 
在循环内left、right不会重合 
 
 
更新方式:left=mid+1, right=mid
为保证搜索区间始终为左闭右开区间
移动左指针时需剔除mid 
移动右指针时无需剔除mid 
 
 
 
 
终止情况:right=left、mid=left/left-1
循环条件、循环内部+1保证:上轮循环
left+1=mid=right-1 
left=mid=right-1 
 
 
越界终止情况:仅左指针剔除mid,仅可能右侧越界
 
 
 
target位置
若存在target:必然在mid处,循环结束只需判断mid 
若不存在target
left=mid=right:循环过程中target可能已不在
搜索区间中,最终位于(mid-1, mid) 
mid+1=left=right:(mid, left) 
left表示小于target元素数量 
 
 
 
 
 
高级版2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 BainarySearchEndReturnV3(nums[0. .n-1 ], target): 	 	 	 	left, right = -1 , n 	while  left < right: 		mid = (left + right + 1 )  		if  nums[mid] == target: 			break  		elif nums[mid] < target: 			right = mid-1  		else : 			left = mid 	if  nums[mid] == target: 		return  mid 	else : 		return  -1  
 
输入判断:无需判断输入列表是否为空
 
初始条件:left, right = -1, n-1
左无需检查,右未检查、需检查 
即代码中查找区间为左开右闭区间 $(left, right]$ 
 
 
中点选择:mid = (left + right + 1) // 2
向上取整  
此处必须选择向上取整,否则left=right-1时进入死循环
(或放宽循环条件,添加尾判断) 
即:循环内检查所有元素,取整一侧指针必须移动  
 
 
循环条件:left < right
搜索区间为左开右闭区间,则检查条件为left < right,
此时搜索区间非空 
在循环内left、right不会重合 
 
 
更新方式:left=mid, right=mid+1
为保证搜索区间始终为左闭右开区间
移动右指针时需剔除mid 
移动左指针时无需剔除mid 
 
 
 
 
终止情况:left=right、mid=left/left+1
循环条件、循环内部+1保证:上轮循环
left+1=mid=right-1 
left+1=mid=right 
 
 
越界终止情况:仅右指针剔除mid,仅可能左侧越界
 
 
 
target位置
若存在target:必然在mid处,循环结束只需判断mid 
若不存在target
left=mid=right:循环过程中target可能已不在
搜索区间中,最终位于(right, right+1) 
mid+1=left=right:(left, right) 
left+1表示小于target元素数量 
 
 
 
 
 
高级版3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 BinarySearchEndReturnV2(nums[0. .n-1 ], target): 	// 折半查找,保持左右指针顺序、不重合 	// 输入:升序数组nums、查找键target 	// 输出:存在返回数组元素下标m,否则返回-1  	left, right = -1 , n 	while  left < right-1 : 		mid = (left + right) // 2  		if  nums[mid] == target: 			break  		elif  nums[mid] < target: 			left = mid 		else : 			right = mid 	if  nums[mid] == target: 		return  mid 	else : 		return  -1  
 
高级版1-原 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 BinarySearchKeep1(nums[0. .n-1 ], target): 	 	 	 	if  n == 0 : 		return  -1  	left, right = 0 , n-1  	while  left < right - 1 : 		mid = floor ((left + right) / 2 ) 		if  nums[mid] == target: 			return  mid 		elif nums[mid] < target: 			right = mid 		else : 			left = mid 	 	if  nums[left] == target: 		return  left 	if  nums[right] == target: 		return  right 	return  -1  
 
初始条件:left = 0, right = n-1 
终止情况 :left = right - 1 
指针移动:left = mid, right= mid 
 
 
关键特点 
n>1时left、mid、right循环内不会重合 
mid可以left、right任意比较,无需顾忌重合 
需要和左右邻居 、left、right比较时适合使用 
扩展使用需要进行真后处理,对left、right进行判断 
 
 
终止情况:left = right - 1
循环过程中会保证查找空间至少有3个元素 ,剩下两个
元素时,循环终止 
循环终止后,left、right不会越界,可以直接检查 
 
 
target位置
 
仅仅二分查找的话 ,后处理其实不能算是真正的后处理
 
 
高级版2-原 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 BinarySearchKeep0(nums[0. .n-1 ], target): 	 	 	 	if  n == 0 : 		return  -1  	left, right = 0 , n 	while  left < right: 		mid = floor ((left + right) / 2 ) 		if  nums[mid] == target: 			return  mid 		elif nums[mid] < target: 			left = mid + 1  		else : 			right = mid 	 	if  left != len(nums) and  nums[left] == target: 		return  left 	return  -1  
 
初始条件:left = 0, right = n,保证n-1可以被正确处理 
终止情况 :left = right 
指针移动:left = mid + 1, right = mid 
中点选择对此有影响,指针移动行为非对称,取ceiling则
  终止情况还有left = right + 1 
 
 
关键特点 
left、right循环内不会重合 
由于floor的特性,mid可以right任意比较,无需
顾忌和right重合 
需要和右邻居 、right比较时适合使用 
 
 
终止情况:left = right
循环过程中会保证查找空间至少有2个元素 ,否则循环
终止 
循环终止条件导致退出循环时,可能left=right=n越界 
 
 
target位置
若存在target,可能位于mid、left=right 
若不存在target,则target位于(left-1, left) 
适合访问目标在数组中索引、及其直接右邻居  
 
 
仅二分查找 ,甚至无需后处理
 
判断nums长度在某些语言中也可以省略
 
 
高级版3-原 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 BinarySearchNoKeep(nums[0. .n-1 ], target): 	 	 	 	if  n == 0 : 		return  -1  	left, right = -1 , n-1  	while  left < right - 1 : 		mid = floor ((left + right) / 2 ) 		if  nums[mid] == target: 			return  mid 		elif nums[mid] < target: 			left = mid 		else : 			right = mid - 1  	 	if  right >= 0  and  nums[right] == target: 		return  right 	return  -1  
 
初始条件:left = -1, right = n-1 
终止情况 :left = right、left = right + 1 
指针移动:left = mid, right = mid - 1 
中点选择对此有影响,指针移动行为非对称,取ceiling则
  同高级版本2 
 
 
特点 
插值查找 Interpolation Search:查找有序数组,在折半查找的基础上考虑
查找键的值
算法 
特点 
子串匹配 蛮力字符串匹配 算法 
将pattern对齐文本前m个字符,从左向右匹配相应字符
m个字符全部匹配,匹配成功,算法停止 
遇到不匹配字符则 
 
 
模式右移1位,然后从模式首个字符开始重复以上匹配 
在n-m位置无法匹配成功,则无足够匹配字符,算法停止 
 
1 2 3 4 5 6 7 8 9 10 11 12 BruteForceStringMatch(T[0. .n-1 ], P[0. .m-1 ]) 	 	 	 	 	for  i = 0  to m-m do  		j = 0  		while  j < m and  P[j] = T[i+j] do  			j = j + 1  			if  j = m 				return  i 	return  -1  
 
特点 
最坏情况下,算法比较次数属于$O(nm)$
即在移动模式之前,算法需要做足m次比较 
但是一般在自然语言中,算法平均效率比最差好得多 
在随机文本中,有线性效率 
 
 
 
Horspool算法 算法从右往左 匹配,在任意位置 匹配不成功时只考虑
同模式最后字符匹配的文本字符c ,确定安全移动距离,在
不会错过匹配子串的情况下移动最长距离
如果c在模式中出现,则模式移动到其最后c至同文本中c
匹配 
否则移动模式长度m 
特别的,如果c只在模式最后字符出现,则也应该移动m 
 
算法 
对给定长度m的模式及文本中用到的字母表,构造移动表t 
将模式同文本开始对齐 
重复以下过程直至发了了匹配子串或模式达到了文本字符外
从模式最后字符开始 ,比较模式、文本相应字符 
若m个字符匹配,停止 
遇到不匹配字符c,若为当前文本中和模式最后匹配字符
对齐的字符,将模式移动t(c)个字符 
 
 
 
1 2 3 4 5 6 7 8 9 10 11 ShiftTable(P[0. .m-1 ]) 	 	 	 	for  i=0  to size-1  do  		Table[i] = m 		 	for  j=0  to m-2  do  		Table[P[j]] = m - 1  - j 		 	return  Table 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 HorspoolMatching(P[0. .m-1 ], T[0. .n-1 ]) 	 	 	 	Table = ShiftTable(P[0. .m-1 ]) 	i = m - 1  	while  i <= n-1  do  		k = 0  		while  k <= m-1  and  P[m-1 -k]=T[i-k] do  			k = k+1  		if  k == m 			return  i-m+1  		else  			i = i+Table[T[i]] 	return  -1  
 
特点 
算法效率
最差情况下模式为相同字符,效率$\in O(nm)$ 
对随机文本效率$\in O(n)$ 
 
 
输入增强
 
 
Boyer-Moore算法 
坏符号移动:模式、文本中相应不匹配字符确定 的移动
(不是Horspool中简单根据最后字符确定移动) 
好后缀移动:模式、文本匹配的后缀确定 的移动 
 
算法 
对给定长度m的模式及文本用到的字母表,构造坏符号移动表t 
对给定长度m的模式构造后缀移动表 
将模式与文本开始处对齐 
重复以下直到找到匹配子串或模式达到文本字符以外
从模式最后字符开始比较模式、文本相应字符 
所有m个字符匹配,则停止 
若c是不匹配字符,移动坏符号表、后缀移动表决定的
距离较大者 
 
 
 
1 2 3 4 5 BadSymbolShift(P[0. .m-1 ]) 	 	 	 
 
特点 
KMP算法 算法从左往右 匹配,失败时不回溯指针,利用已经得到的
部分匹配 结果尽可能将模式滑动一段距离,从模式中间next 
字符开始比较
算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 KMPShift(P[0. .m-1 ]) 	 	 	 	i = -1  		 	j = 0  	next[0 ] = -1  		 	while  j < m 		if  i == -1  or  P[j] == P[i] 			i += 1  			j += 1  			 			next[j] = i 		else  			i = next[i] 			 			 			 	return  next 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 KMPShiftVal(P[0. .m-1 ]) 	 	 	 	i = -1  		 	j = 0  	next_val[0 ] = -1  	while  j < m do  		if  i == -1  or  P[j] == P[i] 			i += 1  			j += 1  			if  T[j] != T[i] 				next_val[j] = i 			else  				next_val[j] = next_val[i] 				 				 		else  			i = next_val[i] 	return  next_val 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 KMPMatching(P[0. .m-1 ], T[0. .n-1 ]) 	 	 	 	i = 0  	j = 0  	while  i < m and  j < n do  		if  i == -1  or  P[i] == T[j] 			i += 1  			j += 1  		else  			i = next[i] 	if  i >= m 		return  j - m + 1  	else  		return  -1  
 
特点