二分查找

121 阅读2分钟

定义

二分查找算法在维基百科中的定义如下:

计算机科学中,二分查找算法(英语:binary search algorithm),也称折半搜索算法(英语:half-interval search algorithm)[1]对数搜索算法(英语:logarithmic search algorithm)[2],是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。

从上述定义可以知道,二分查找算法的前提条件是:查找的数组必须是一个有序数。

算法步骤

  1. 从数组中可知对比范围是 [start,end][start,end] (startstartendend 的闭区间)

  2. 由左右边界可求出中间值的索引值为 midmid

  3. 将目标值 targettargetmidmid 进行对比,并判断 targettargetmidmid的大小,根据结果作出以下行动

    • 相等,终止查找,输出 midmid 的索引值
    • targettarget 大于 midmid,令 startstart 等于 mid+1mid + 1,重复步骤 1
    • targettarget 小于 midmid,令 endend 等于 mid1mid - 1,重复步骤 1

流程图

未命名文件 (8).png

代码实现

/**
 * 查找数据中特定的值
 * @param {Array<number>}  arr   数组
 * @param {number}         target 需要查找的值
 * @returns {number}       数组中等于target的索引值
 */
function binarySearch(arr, target) {
    let start = 0;
    let end = arr.length - 1;
    let mid;

    while (start <= end) {
        mid = start + Math.floor((end - start) / 2) ;
        if (arr[mid] === target) {
            return mid;
        } else if (arr[mid] > target) {
            end = mid - 1;
        } else if (arr[mid] < target) {
            start = mid + 1;
        }
    }
    return -1;
}

let arr = [1, 2, 3,7,9,44,55,312,777,7892];

let target = 7892;
console.log(binarySearch(arr, target));

扩展

在求一些特殊的结果时,可以使用二分查找而忽略这个前提条件,如下例

给定一个无序数组 nums[],相邻数字不同。返回任意一个局部最小值。局部最小值的定义为:

  1. 对于第一个元素 而言,只要它比 第二个元素要小,则就是局部最小值。
  2. 对于最后一个元素 而言,只要它比 倒数第二个元素要小,则就是局部最小值。
  3. 对于数据中间的元素,如果一个数相邻的两个数都比他大,那么他就是局部最小值。

上述条件如下图

image.png

如果你还记得抛物线,这就是找一个开口朝上的抛物线。下面说说解决具体思路

无标题-2021-10-13-2331.png

代码实现

function LocalMinimum (arr, i) {
	if (arr[0] < arr[1]) {
            return {
                value: [arr[0], arr[1]], 
                index: 0
            }
        };
	if (arr[arr.length - 2] > arr[arr.length - 1]) {
            return {
                value: [arr[arr.length - 2], arr[arr.length - 1]], 
                index: arr.length - 1
            };
        }

	let start = 0;
	let end = arr.length - 1;

	while (start < end) {
		let mid = start + ((end - start) >> 2);
		if (arr[mid - 1] < arr[mid]) {
			end = mid;
		}else if (arr[mid + 1] < arr[mid]) {
			start = mid;
		}else {
			return {
                            value: [arr[mid - 1], arr[mid], arr[mid + 1]], 
                            index: mid,
                        };
		}
	}
}