题目链接:leetcode.cn/problems/binary-search/description/ https://leetcode.c…
文章讲解:programmercarl.com/0704.%E4%BA… programmercarl.com/0027.%E7%A7…
704.二分查找
首先,注意使用条件:
- 不重复
- 升序
此外,为了避免溢出,left + right 可以用 left + (right-left)代替。
二分查找本身算法思路很简单,初中就学过:找中间值,然后与目标值比大小,收缩单侧区间。
1.左闭右闭
难点在于思考清楚:left = middle + 1;到底要不要加这个1(right的-1同理);以及,
right = nums.length - 1要不要减这个1
当前nums[middle]已经不是满足条件的值了,而我们采用的是闭区间,那就意味着下次循环的两端值都要能取到,所以就不能再直接用middle值了,故而左右需要分别+1和-1。
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
// 左闭右闭区间 [left, right]
let left = 0, right = nums.length - 1;
while(left <= right) {
// 右移一位位运算是除以2,防止相加时溢出
let middle = left + ((right - left) >> 1);
if (nums[middle] < target) {
left = middle + 1;
} else if (nums[middle] > target) {
right = middle - 1;
} else {
return middle;
}
}
return -1;
};
2.左闭右开
右边开区间,就意味着右边值取不到,所以right = nums.length & while (left < right) & right = middle
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
// 左闭右开 [left, right)
let left = 0, right = nums.length
while (left < right) {
let middle = left + ((right - left) >> 1);
if (nums[middle] < target) {
left = middle + 1;
} else if (nums[middle] > target) {
right = middle;
} else {
return middle;
}
}
return -1;
};
27.移除元素
做这道题需要理解数组的结构,是连续存储,删除元素的本质是用后面的元素覆盖这个元素。
1.暴力双层循环
这种方法思路简单,但有一些细节需要注意。
外层循环用于遍历元素,内层循环对符合条件的元素进行后覆盖前的操作。 注意内层循环的索引j,如果让j=i的话,j+1实际就超过了i。所以,让j = i+1, 然后nums[j - 1] = nums[j]
另外,所有后面的元素都往前移动了一位,所以需要i--,从当前元素位置开始继续向后遍历。如果没有i--,则可能漏掉后一个元素。比如[3, 3, 1, 2], val:3,可能就会漏掉第二个3。
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
var removeElement = function(nums, val) {
let length = nums.length;
for(let i = 0; i < length; i++) {
if (nums[i] === val) {
for (let j = i + 1; j < length; j++) {
nums[j - 1] = nums[j]
}
i--;
length--;
}
}
return length;
};
2.同向双指针(快慢指针)
这种算法难在思路:即用一次循环,两个快慢指针,在原数组上构建出一个不含目标元素的新数组。 slow是新数组的下标,所以如果不是val,就需要将其加入这个新数组,并且slow++。
其实核心就是,想象用slow构建出一个全新的数组。
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
// 双指针(快慢指针法)
var removeElement = function(nums, val) {
let size = nums.length;
let slow = 0; // 新数组
for (let fast = 0; fast < size; fast++) {
if (nums[fast] !== val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
};