二分查找
题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target,写一个函数搜索 nums 中的 target,如果目标值存在,返回下标,否则返回 -1。
代码
1. 左闭右闭
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right)
{
int mid = left + (right - left) / 2; // think why?
if (nums[mid] > target)
right = mid - 1;
else if (nums[mid] < target)
left = mid + 1;
else // find target
return mid;
}
return -1;
}
};
采用左闭右闭的方式:搜索区间 [left, right] 。
对于这种左闭右闭的区间来说,左端点值等于右端点值的情况是可以的,因此while循环判断条件写的是 left <= right,而不是 left < right;
而对于下面的左闭右开区间来说,左端点值等于右端点值的情况是不可以的,[num, num)是一个空集,因此while循环判断条件写的是left < right,而不是 left <= right。
一般推荐左闭右闭这种写法。
下面给出采用左闭右开方式的代码。
2. 左闭右开
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size();
while (left < right)
{
int mid = left + (right - left) / 2; // think why?
if (nums[mid] > target)
right = mid;
else if (nums[mid] < target)
left = mid + 1;
else // find target
return mid;
}
return -1;
}
};
常见问题
1. int mid = left + (right - left) / 2
为什么要这样写?为什么不写成 mid = (left + right) / 2 ?
主要原因是防止 left + right 溢出,即两者之和超出了int的上限,后面再除以2就没意义了。
int的范围是 -2147483648~2147483647。
举个例子,left的值为2147483640,right的值为2147483642,此时 left + right 的值为4294867282,远远超出了int的上限,而采用 left + (right - left) / 2 的方式,首先计算 right - left 得2,接着除以2得1,最终 left + 1 得2147483641。
只要right不超过int上限,那么采用 left + (right - left) / 2 方式就不会出现溢出现象。
2. while循环中是 <= 还是 <
关键在于前面right的定义,取决于 right = n - 1 还是 right = n,其中n为数组长度。
right = n - 1 表示搜索区间从 left 到 right;right = n 表示搜索区间从 left 到 right - 1。
具体分析可参考“代码”。
“35 搜索插入位置”和“704 二分查找”基本相同
移除元素
题目描述:给定一个数组 nums 和一个值 val,需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。
要求:空间复杂度:O(1)
本题主要是数组中双指针的使用,更准确说是快慢指针。
具体做法是让快指针fast先走,如果遇到的是不重复的元素,把fast指针指向的元素的值传给慢指针slow指向的元素,然后慢指针slow往前走一步,重复以上操作直至快指针走完整个数组。
无论是不是重复元素,快指针fast都会向前走一步;而只有当不是重复元素时,慢指针slow才会向前走一步。
若刚开始时slow和fast的值均置0,最终slow的值即为数组的新长度。
代码
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0, fast = 0;
while (fast < nums.size())
{
if (nums[fast] != val)
{
nums[slow++] = nums[fast];
}
fast++;
}
return slow;
}
};
在有关数组的算法问题中,经常会用到双指针。一般有三种情况:从两头到中间、从中间到两头和快慢指针。
解决二分查找也是考双指针,从两头到中间,int left = 0, right = n - 1。