数组理论基础,704. 二分查找,27. 移除元素
二分查找
关键是确定代码里面,数组表示合法区间
二分查找涉及的很多的边界条件,
- 到底是
while(left < right)
还是while(left <= right)
- 到底是
right = middle
呢,还是要right = middle - 1
按照数组表示的合法区间可以表示为**‘左闭右闭’** 或者**‘左闭右开’**,即
[left,right]
[left,right)
左闭右闭 [left,right]
此番情况下,由于是闭区间,所以left是可以等于right的,left=right在闭区间里是合法的,所以最初while循环判断的时候left是可以等于right的,即
while(left <= right)
正因为存在left=right的情况,所以需要将right取到数组的最后一个下标值即right = nums.length-1
int left=0,right=nums.length-1;
while(left <= right){
int middle = left + (right - left) / 2;// 防止溢出 等同于(left + right)/2
if(nums[middle]==target){
return middle;
}
}
接下来关键的是left和right的更新
如果target在middle的右半部分,则left需要向后移动一个单位,即left=middle+1
if(nums[middle]<target){
left=middle+1;
}
如果target在middle的左半部分,因为我们的数组区间表示为左闭右闭,且nums[middle]这个值是>target的,所以right需要向前移动一个单位,即right=middle-1
if(nums[middle]>target){
right=middle-1;
}
所以完整代码为
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
左闭右开
此番情况下,由于是右边是开区间,所以left不可以等于right的,left=right在闭区间里是不合法的,所以最初while循环判断的时候left不可以等于right的,即
while(left < right)
正因为不存在left=right的情况,所以需要将right取不到数组的最后一个下标值即right = nums.length
int left=0,right=nums.length;
while(left < right){
int middle = left + (right - left) / 2;// 防止溢出 等同于(left + right)/2
if(nums[middle]==target){
return middle;
}
}
接下来关键的是left和right的更新
如果target在middle的右半部分,左边是闭区间,则left需要向后移动一个单位,即left=middle+1
if(nums[middle]<target){
left=middle+1;
}
如果target在middle的左半部分,因为我们的数组区间表示为左闭右开,且nums[middle]这个值是>target的,所以right就是middle,即right=middle
if(nums[middle]>target){
right=middle;
}
所以完整代码为
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
int middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle; // target 在左区间,在[left, middle)中
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,在[middle + 1, right)中
} else { // nums[middle] == target
return middle; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
移除元素
本题引入双指针的思想,一个快指针,一个慢指针
-
快指针用于遍历原来数组的下标
-
慢指针用于更新新数组的下标
本质上是对同一个数组的更新与覆盖操作
class Solution {
public int removeElement(int[] nums, int val) {
// 快慢指针
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if (nums[fastIndex] != val) {
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
}