二分查找算法类似我们日常生活中常见的猜数字的游戏。假设给定从1-100这个排好序的数组,而猜数字的人只知道这个范围,你拿着一块写有数字的题板背对着他,让他猜你手里拿的的数字。
估计有经验的人会先猜50,然后你说偏大了,偏小了,还是相等。如果偏大了或者是偏小了,他会用同样的方式在偏大或者偏小的数组中从新指定一个中间值来猜。。就这样不断重复如下去。。直到找到要猜的数字为止。
类似图如下:
算法分析如下:
(1) 将数组的第一个位置设置为下边界( 0 )。
(2) 将数组最后一个元素所在的位置设置为上边界(数组的长度减 1 )。
(3) 若下边界等于或小于上边界,则做如下操作。
a. 将中点设置为(上边界加上下边界)除以 2 。
b. 如果中点的元素小于查询的值,则将下边界设置为中点元素所在下标加 1 。
c. 如果中点的元素大于查询的值,则将上边界设置为中点元素所在下标减 1 。
d. 如果没有找到,重复执行a,b,c.直到找到为止。否则中点元素即为要查找的数据,可以进行返回。
算法代码如下:
// 插入排序算法 var insertionSort = function (arr) { var temp, inner; for (var outer = 1; outer <= arr.length-1; ++outer) { temp = arr[outer]; inner = outer; while (inner > 0 && (arr[inner-1] >= temp)) { arr[inner] = arr[inner-1]; --inner; } arr[inner] = temp; } }; // 二分查找算法 var binSearch = function (arr, data){ var upperBound = arr.length - 1; var lowerBound = 0; while(lowerBound <= upperBound){ var mid = Math.floor((upperBound + lowerBound) / 2); console.log("当前中间点" + mid); if (arr[mid] < data){ lowerBound = mid + 1; }else if (arr[mid] > data){ upperBound = mid - 1; } else { return mid; } } return -1; };
var nums = [5,1,7,4,2,10,9,3,6,8]; console.log(nums.toString()); insertionSort(nums); console.log(nums.toString()); console.log(nums[binSearch(nums, 6)]);
结果如下:
有一种情况我们似乎没有考虑到,那就是当数组中出现多个重复数据的情况下,怎么来确定我们想要查找的数据的出现次数。
通过分析我们知道要查找的数字6的开始索引为5,结束索引为8,通过Math.floor取中间值的时候得到的索引就是7.也就是说7是处在5和8之间的一个数。所以我们可以利用这个7来遍历左右两边来查找重复值。
var sameCount = function (arr, data){ var count = 0; var position = binSearch(arr, data); if (position > -1){ ++count; for (var i = (position - 1); i >= 0; --i){ if (arr[i] == data){ ++count; } else { break; } } for (var j = (position + 1); j < arr.length; ++j){ if (arr[j] == data){ ++count; } else { break; } } } return count; };
运行:
var nums = [5,1,7,4,2,10,9,3,6,8,6,7,6,11,6]; console.log(nums.toString()); insertionSort(nums); console.log(nums.toString()); console.log(sameCount(nums, 6));
结果为: