折半查找(二分查找)
概念
每次取中间记录查找的方法。
前提
线性表中的记录必须是关键码有序(通常从小到大有序),线性表必须采用顺序存储。
基本思想
在有序表中,取中间记录作为比较对象:
- 若给定值与中间记录的关键字相等,则查找成功;
- 若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;
- 若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。
不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。
算法实现
function Binary_Search(a,key){
var low,high,mid;
low = 0; /* 定义最低下标为记录首 */
high = a.length-1; /* 定义最高下标为记录尾 */
while (low <= high) {
mid = parseInt((low + high) / 2); /* 折半 */
if (key < a[mid]) { /* 若查找值比中值小 */
high = mid - 1; /* 最高下表调整到中间下标小一位 */
}else if(key >a[mid]){ /* 若查找值比中值大 */
low = mid + 1; /* 最低下表调整到中间下标大一位 */
}else{
return mid; /* 若相等则说明 mid 即为查找到的位置 */
}
}
return a.length; /* 返回原数组长度则说明查找失败 */
}
时间复杂度分析
折半算法的时间复杂度为 O(logn) 。
对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,因此不建议使用折半算法。
插值查找
基本思想
折半查找最关键的一段代码就是求中间值:
mid=low+high2=low+12(high−low)
也就是 mid 等于最低下标 low 加上最高下标 high 与 low 的差的一半。
考虑将这个 12 改进为下面的计算方案:
mid=low+key−a[low]a[high]−a[low](high−low)
假设 a[11]=1,16,24,35,47,59,62,73,88,99,low=0,high=9,则a[low]=0,a[high]=99 ,如果要找的是 key=16 ,按照折半的做法,需要四次才可以得到结果。
但如果用新方法: key−a[low]a[high]−a[low]=16−199−1≈0.153,即mid≈1+0.153×(9−0)=2.377 ,取整 mid=2 ,则只需要两次就查找到结果了,大大提高了查找的效率。
插值查找是根据要查找的关键字 key 与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式: key−a[low]a[high]−a[low]
算法实现
/* 插值查找 */
function Interpolation_search(a,key){
var low,high,mid;
low = 0; /* 定义最低下标为记录首 */
high = a.length-1; /* 定义最高下标为记录尾 */
while (low <= high) {
mid = parseInt(low + (key -a[low]) / (a[high]-a[low])*(high-low)); /* 插值 */
if (key < a[mid]) { /* 若查找值比中值小 */
high = mid - 1; /* 最高下表调整到中间下标小一位 */
}else if(key >a[mid]){ /* 若查找值比中值大 */
low = mid + 1; /* 最低下表调整到中间下标大一位 */
}else{
return mid; /* 若相等则说明 mid 即为查找到的位置 */
}
}
return a.length; /* 返回原数组长度则说明查找失败 */
}
时间复杂度分析
也是 O(logn) 。但对于表长较长,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好得多。