二分查找的两种情况
1.左闭右闭
区分理解:可以把区间想象成只有一个元素,当区间只有一个元素的时候如[2,2],这时候这时候<=(可以取‘=’)才能满足左闭右闭
但是当区间为[2,2),**左闭右开**的时候,取<=则无法满足右开(因为右边是取不到的),所以此时只能取<
代码实现:
class Solution{
public:
int search(vector<int>& nums,int target){
int left=0;
int right=nums.size()-1;
while(left<=rhght){ //当left==rhght.区间[left,rhght]仍然有效,所以用<=
int middle=left+(right-left)/2; //等价于(left+right)/2
if(nums[middle]<target)
left=middle+1;
else if(nums[middle]>target)
right=middle-1;
else
return -1;
}
//未找到目标值
return -1;
}
}
2.左闭右开
因为符号是[ , ),条件为left<right,若取了’=‘则不满足’)'右开,注意在二分的时候,也要满足左闭右开原则
class Solution{
public:
int search(vector<int>& nums,int target){
int left=0;
int right=nums.size(); //因为是又开,取不到nums.size()
while(left<rhght){ //当left≠rhght.否则只有一个元素的时候,不满足左闭右开,因为右开
int middle=left+(right-left)/2; //等价于(left+right)/2
if(nums[middle]<target)
left=middle+1;
else if(nums[middle]>target)
right=middle; //满足左闭右开的右开,取不到right
else
return -1;
}
//未找到目标值
return -1;
}
}
结论:无论左闭右开,还是左闭右闭,在划分的时候区间都要满足左闭右开或者左闭右闭。
移除元素
暴力拆解法:
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
};
时间复杂度:O(n^2)
空间复杂度:O(1)
双指针法:
题目:https://leetcode.cn/problems/remove-element/description/
相向双指针法:
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
* 时间复杂度:O(n)
* 空间复杂度:O(1)
*/
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int leftIndex = 0;
int rightIndex = nums.size() - 1;
while (leftIndex <= rightIndex) { //这里等号要带=号,否则只会指向第二个元素位置
// 找左边等于val的元素
while (leftIndex <= rightIndex && nums[leftIndex] != val){ //这里和下面一定要加leftIndex <= rightIndex
//否则测试用例为 3 2 2 3 的时候,会指向第二个3
++leftIndex;
}
// 找右边不等于val的元素
while (leftIndex <= rightIndex && nums[rightIndex] == val) {
-- rightIndex;
}
// 将右边不等于val的元素覆盖左边等于val的元素
if (leftIndex < rightIndex) {
nums[leftIndex++] = nums[rightIndex--];
}
}
return leftIndex; // leftIndex一定指向了最终数组末尾的下一个元素
}
};
快慢指针法:
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
个人感觉快慢指针比边界好一点,因为不用考虑边界问题。。。