JS-7种查找算法之顺序查找、二分查找、插值查找、斐波那契查找
参考链接 https://www.cnblogs.com/yw09041432/p/5908444.html
1.顺序查找
说明:顺序查找适合于存储结构为顺序存储或链接存储的线性表。
基本思想:顺序查找也称为线形查找,属于无序查找算法。从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于k的结点,表示查找失败。
复杂度分析:
查找成功时的平均查找长度为:(假设每个数据元素的概率相等) ASL = 1/n(1+2+3+…+n) = (n+1)/2 ;
当查找不成功时,需要n+1次比较,时间复杂度为O(n);
所以,顺序查找的时间复杂度为O(n)。
1 /** 2 * 3 * @param {被查找数组} arr 4 * @param {查找的关键值} value 5 */ 6 function SequenceSearch(arr, value){ 7 for(let i = 0; i < arr.length; i++){ 8 if (arr[i] == value){ 9 return i; 10 } 11 } 12 return - 1; 13 }
2.二分查找
二分查找 也为折半查找
首先要找到一个中间值,通过与中间值比较,大的放又,小的放在左边。再在两边中寻找中间值,持续以上操作,直到找到所在位置为止
找不到返回false
1 // 递归 2 function binarySearch(data, dest, start, end){
if (start > end){ // 新增否则找不到进入死循环了
return false;
} 3 var end = end || data.length - 1; 4 var start = start || 0; 5 var m = Math.floor((start + end) / 2); 6 //直接命中 7 if (data[m] == dest){ 8 return m; 9 } 10 11 12 if (data[m] > dest){ // 放左 13 end = m - 1; 14 return binarySearch(data, dest, start, end); 15 }else{ // 放右 16 start = m + 1; 17 return binarySearch(data, dest, start, end); 18 } 19 return false; 20 } 21 22 // 非递归 用while 23 //代码中的判断条件必须是while (left <= right), 24 //否则的话判断条件不完整,比如:array[3] = {1, 3, 5}; 25 //待查找的键为5,此时在(low < high)条件下就会找不到,因为low和high相等时,指向元素5,但是此时条件不成立,没有进入while()中 26 27 function binarySearch2(data, dest){ 28 var end = data.length - 1; 29 var start = 0; 30 while(start <= end){ 31 var m = Math.floor((end + 1) / 2); 32 if(data[m] == dest){ 33 return m; 34 } 35 if (data[m] > dest){ 36 end = m - 1; 37 }else{ 38 start = m + 1; 39 } 40 } 41 return false 42 }
3. 插值查找
将二分查找的点改进为 mid=low+(key-a[low])/(a[high]-a[low])*(high-low)
基本思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。
注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
复杂度分析:查找成功或者失败的时间复杂度均为O(log2(log2n))。
1 function InsertionSearch(arr, val, start, end){ 2 var end = end || data.length - 1; 3 var start = start || 0; 4 5 var mid = start + (val - arr[low]) / (arr[end] - arr[start]) * (end - start); 6 if(arr[mid] == val){ 7 return mid; 8 } 9 10 if(arr[mid] > val){ 11 return InsertionSearch(arr, val, start, mid - 1); 12 } 13 else{ 14 return InsertionSearch(arr, val, mid + 1, end); 15 } 16 }
4. 斐波那契查找
斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(从第三个数开始,后边每一个数都是前两个数的和)
1 // 斐波那契数组的实现 2 function getNum1(index){ 3 if (index == 1 || index == 2){ 4 return 1; 5 }else{ 6 return getNum(index - 1) + getNum(index - 2); 7 } 8 } 9 10 function getNum2(index){ 11 if (index == 1 || index == 2){ 12 return 1; 13 }else{ 14 var one = 1; 15 var two = 1; 16 for (var i = 3; i <= index; i++) 17 { 18 if (i == 3) 19 { 20 one = 1; 21 two = 1; 22 } 23 else 24 { 25 var temp = one; 26 one = two; 27 two = temp + two; 28 } 29 } 30 return one + two 31 } 32 } 33 34 function getNum3(index) 35 { 36 var F = []; 37 F[0]=0; 38 F[1]=1; 39 for(var i=2; i < index - 1; i++){ 40 F[i]=F[i-1]+F[i-2]; 41 } 42 return F[index]; 43 }
顾名思义,该查找方法利用了斐波那契数列
该方法的主要思路为,先在斐波那契数列F中找到第k项,使其满足,F[k]-1 > 有序数组的最大索引号 > F[k-1]-1,然后将数组扩充到长度为F[K]-1,并使扩充项的值都等于有序数组的最后一项。
分割点的索引为mid = low + F[K-1]-1,此时有序数组被mid划分为两段,左段长度为F[K-1]-1,右段长度为F[k-2]-1。
若查找值大于mid值,则low等于mid+1,而k = k - 2;若查找值小于mid,则high = mid -1,k =k -1.
1 function search(array, value){ 2 let low = 0, high = array.length - 1, n = array.length - 1; 3 let mid, k = 0; 4 5 // 构建一个长度大于array数组的斐波那契数组 6 var F = []; 7 F[0]=0; 8 F[1]=1; 9 for(var i=2; i < high + 5; i++){ 10 F[i]=F[i-1]+F[i-2]; 11 } 12 13 while(high > F[k] - 1){ //寻找第k项 14 k++; 15 } 16 17 for (let i=high; i<F[k]-1; i++){ //补全有序数组 18 array[i] = array[high]; 19 } 20 21 while (low <= high){ 22 mid = low + F[k - 1] - 1; 23 if (array[mid] > value){ 24 high = mid - 1; 25 k = k - 1; //长度缩减为 F[k-1] -1 26 }else if(array[mid] < value) 27 { 28 low = mid + 1; 29 k = k - 2; //长度缩减为 F[k-2] -1 30 }else{ 31 if (mid <= n) //相等则找到位置 32 return mid; 33 else //大于原始长度,则说明等于数组最后一项 34 return n; 35 } 36 } 37 return -1; 38 }