二分查找
不知道大家有没有看过《超级无敌奖门人》的综艺,里面有一个和这个题目很相似的游戏:从1~100里面说一个数字,说中了数字后气球爆炸,没说中的话数字范围收缩。
这个题目的关键解法也是让数字范围收缩,效率最快的收缩方法是:数字范围收缩成一半。如下图,为了找target=2,数字范围不断减半。

左闭右闭与左闭右开
二分查找的算法并不难,但是一开始做的时候边界问题处理起来比较麻烦。解决办法是确定数组两边的开闭,即遍历的时候是否包括数组左右两边的数字。网上的算法都是左闭右闭或者左闭右开的。我们一般写一个数据的循环遍历就是左闭右开的:
const array = [1, 2, 3, 4, 5];
for (let i = 0; i < array.length; i++) {
// 数组范围包括0但不包括array.length,即范围是[0, array.length)
}
实现代码:
var search = function(nums, target) {
// right是数组最后一个数的下标+1,nums[right]不在查找范围内,是左闭右开区间
let mid, left = 0, right = nums.length;
// 当left=right时,由于nums[right]不在查找范围,所以不必包括此情况
while (left < right) {
// 位运算 + 防止大数溢出
mid = left + ((right - left) >> 1);
// 如果中间值大于目标值,中间值不应在下次查找的范围内,但中间值的前一个值应在;
// 由于right本来就不在查找范围内,所以将右边界更新为中间值,如果更新右边界为mid-1则将中间值的前一个值也踢出了下次寻找范围
if (nums[mid] > target) {
right = mid; // 去左区间寻找
} else if (nums[mid] < target) {
left = mid + 1; // 去右区间寻找
} else {
return mid;
}
}
return -1;
};
移除元素
本题可以使用javascript的Array的splice方法删除元素的方法解决,但是算法复杂度会变成O(n^2)。为了达到算法复杂度为O(n)的目的,使用快慢指针可解。
我们定义了两个指针:快指针和慢指针。快指针表示整个数组循环的指针,慢指针表示删除了目标值的新数组的下标。慢指针只有在快指针的值不为target的值时才移动:

var removeElement = function (nums, val) {
let slow = 0;
for(let i = 0; i < nums.length; i++) {
// 不等于目标值的时候,slow指针移动
if (nums[i] !== val) {
nums[slow] = nums[i];
slow++;
}
}
return slow;
};
总结
这两题其实都很容易想到暴力的解法,但是好的算法都是想办法减少没必要的计算。像上面的二分查找和快慢指针运用场景很广泛,各位可以尝试使用一下。
本文详细介绍了LeetCode中二分查找的实现,包括左闭右闭与左闭右开的边界处理,并通过示例代码进行说明。此外,还探讨了如何使用快慢指针高效地移除数组元素,降低算法复杂度到O(n)。通过这两题,作者鼓励读者思考并运用优化算法来避免不必要的计算。
2174





