704. 二分查找
何为二分法
二分法,也称为折半法,是一种在有序数组中查找特定元素的搜索算法。
二分法的时间复杂度是O (logn) 。
所以在算法中,比O (n)更优的时间复杂度几乎只能是O (logn)的二分法。 根据时间复杂度来倒推算法也是面试中的常用策略:题目中若要求算法的时间复杂度是O (logn),那么这个算法基本上就是二分法。
算法时间复杂的的一个排行如下所示
O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(n^2)平方阶 < O(n^3)立方阶 < O(2^n)指数阶
算法思路
二分法查找的思路如下:
- 首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
- 如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤第一步的操作。
- 如果某一步数组为空,则表示找不到目标元素。
二分法查找的时间复杂度O(logn)。
注意
- 是从“有序”数组里面寻找
- 左右区间一般都是“左闭右闭”或者“左闭右开”。
- 下标:left:0,right: arr.length-1
- 但是二分查找真正的坑根本就不是那个细节问题,而是在于到底要给
mid加一还是减一,while 里到底用<=还是<。
采取“左闭右闭”的写法
var search = function(nums, target) {
// 左闭右闭
let left = 0;
let right = nums.length - 1;
while (left <= right) {
let mid = (left + right) >> 1; // 关注点
if (nums[mid] == target) {
return mid
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
}
}
return -1
};
采取“左闭右开” 的写法
var search = function(nums, target) {
// 左闭右开
var left = 0;
var right = nums.length;
while(left < right) {
var mid = (left + right) >> 1;
if(nums[mid] == target) {
return mid
} else if (nums[mid] > target) {
right = mid;
} else if (nums[mid] < target) {
left = mid + 1;
}
}
};
总结 & 注意点
mid=(left+right)>>1的含义
右移运算符>>,运算结果正好能对应一个整数的二分之一值,这就正好能代替数学上的除2运算,但是比除2运算要快。
mid=(left+right)>>1相当于mid=(left+right)/2
JavaScript 中 的变量 是松散类型 的,可以保存任何类型数据,变量只不过是一个名称。
所以当出现left=0,right=5时,直接使用(left + right) / 2得到的就是2.5 。
那么当值为undefined时,下列的if或者else if中条件都会不符合,这样left和right的值都不会发生变化,由此,就会一直在while中死循环。
27. 移除元素
像 C/C++ 这种传统的编译型的语言,它们的数组在内存中用一串连续的区域来存放一些值,而且它们的数组中存放的数据类型都需要预先设定成同一类型。
而对于JS来说的话,数组中存储的数据类型是可以完全不一致的,这就意味着,JS 数组中内存地址不是连续的。不过,现在的 JS 引擎为了优化 JS 的性能,它会分配一个连续的内存空间给存储了相同数据类型的数组,以达到更好的遍历效果。所以,只要你数组里存的是相同类型的值,在内存中的地址还是连续的。
js删除数组中某一元素的方法:首先获取指定元素在数组中的位置(即索引index);然后使用splice()函数根据索引值来删除数组中的元素,语法格式
splice(index, 1)。
错误思路:采用暴力解法的话
一开始想的的是使用for循环遍历数组中的每一个元素,用splice方法,把数组中和val的值给删除掉。但是遇到的问题是,let nums = [0, 1, 2, 2, 3, 0, 4, 2], val = 2;两个连续的2,当使用splice方法删除第一个2(元素下标为2)时,原数组中元素下标都会前移一位。第二个2的下标就会由3变成了2,但是此时继续进行下一轮遍历了,i=3,就会忽略元素中的第二个2 。
后续的解决方法就是,
nums.splice(i,1);i--;len--;不过这样的写法内存消耗大。
后来想到把val值换成其他字符,然后删掉。首先考虑的是replace,但是!replace()方法可以实现替换字符串操作。不能对数组进行操作
var removeElement = function(nums, val) {
let len = nums.length;
for(let i = 0; i < len; i++) {
if(nums[i] == val) {
nums.splice(i,1)
i--;
len--;
}
}
console.log(nums)
};