yi 二分查找法 LeetCode704
通过卡哥的讲解将二分法进行梳理:有时候没有注意到那么多问题
常用的情况分为两种区间为左闭右闭和左闭右开情况。
(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;
}
};
(2)左闭右开
// 版本二
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;
}
};
问题点:
1.right值的确定: 容易出现问题,核心在元素个数和可以访问的元素
在左闭右闭的情况,设置right=size-1就可以访问到所有的元素
但是在左闭右开的情况,因为right不能取到,要求所有的元素都能被访问,则一开始right等于size,也就是元素最后一个值之后的一个元素
2.while()中left和right中<与<=的确定:通过特例容易理解
【1,1】在1中可以成立,但是在2中不能够成立,这也就是=的区别
3.分为三种情况对下标进行返回:==单列
4.两种情况中nums[mid]<right的情况不同:容易理解,容易忽略。
er 移除元素 LeetCode27
比较简单经典的问题
(1)双重for循环
// 时间复杂度: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;
}
};
问题点:
1.对于j的书写方式,感觉上应该是for(int j = i; j<size; ++j)也是可以的,但是因为nums
[size]处的元素并没有意义,在LeetCode中不能通过。
2.--i容易被忘记,因为整体向前移动所以需要在移动完成以后进行--i操作,使得从未判断的位置继续进行判断。
3.开始使用size代替nums.size()是为了便于书写
(2)双指针的方式
双指针的方式思路比较难想
// 时间复杂度: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;
}
};
核心思想:快慢指针也许可以帮助消除一层for循环
问题:
1.搞清if的条件,理解原理:fast指针是为了遍历所有的元素,slow指针是为了保存不含val的元素。因此fast一直向前走,slow遇到当fast与val不相同的情况才进行保存即向前移动。
2.最终的返回时slow,可以带入例子验证,slow是落在最终数组的后面的一个位置。
补充基础知识:
1.vector的底层是数组,实际上也就是三个指针。
2.常常用到的vector.erase() 的时间复杂度是O(n),所以当数据量比较大的时候不要忘记这个问题。