各位网友好!我打算在这记录关于我自己刷算法的过程和收获,顺便让自己养成写博客的习惯。
目录
704.二分查找
链接: 二分查找
思路
因为 数组nums 是升序数组,要在升序数组中查找目标值并返回下标,并且这个数组可以认为没有重复的元素。我们的第一想法是:可以让目标值target与数组中的每一个元素进行比较,如果相等就返回该数组元素的下标,没有找到就返回-1.但这样的时间复杂度就比较高了,我们可以使用二分搜索的方法来解决此题,大大缩减了时间的复杂度。
解题方法
按照搜索区间的不同,分为左闭右开和左闭右闭2种方法。
- 需要注意的是,在最开始定义两个变量来确定搜索区间的时候,左闭右闭的写法中,右端点需要-1,while循环的判断条件是left <= right;而左闭右开的写法右端点无需-1,while循环的判断条件是left < right.
复杂度
- 时间复杂度:O(logn),其中 n 是数组的长度。
- 空间复杂度:O(1)。
Code
方法一 - 左闭右闭
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 注意
while (left <= right) { // 注意
int mid = left + (right - left) / 2; //防止溢出
if (nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
//未找到目标值
return -1;
}
};
方法二 - 左闭右开
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); // 注意
while (left < right) { // 注意
int mid = left + (right - left) / 2; //防止溢出
if (nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid;
}
//未找到目标值
return -1;
}
};
两者的区别
while中的 < 和 <=
得看搜索区间,
- 左闭右闭:
nums.size() - 1,即最后一个元素的索引。因此每次循环的 搜索区间 是 [left, right] 左闭右开。 - 左闭右开:
nums.size() ,即最后一个元素的索引的下一个元素。因此每次循环的 搜索区间 是 [left, right])左闭右开。如果left == right,那么此时搜索区间[left, left)为空,即是一个无效的区间。
第三个if块中right = mid - 1 和 right = mid
若搜索区间是两端都闭的,即 [left, right]。那么当我们发现索引 mid 不是要找的 target 时,下一步应该去搜索区间 [left, mid-1] 或者区间 [mid+1, right] ,因为 mid 已经搜索过,应该从搜索区间中去除。若搜索区间左闭右开,则 mid 是没没有搜索过的,所以无需-1.
27.移除元素
链接: 移除元素
思路
我们第一眼的思路是,我们直接 new 一个 int[] 数组,把所有不等于val的的元素放进这个新数组中,然后返回这个新数组即可。
但是现在题目让你原地移除,不允许 new 新数组,只能在原数组上操作,然后返回一个长度。
如果直接操作,用目标值val与原数组进行比较,如果不相等,则将这个数组的元素放到新数组中。解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。但时间的复杂度为O(n^2).虽然也能通过,但更高效的方法是使用双指针之快慢指针。所谓快慢指针,就是两个指针同向而行,一快一慢。
注:
在数组中并没有真正意义上的指针,但我们可以把索引当做数组中的指针,这样也可以在数组中施展双指针技巧
解题方法
定义两个变量作为快慢指针,如果 fast 遇到值为 val 的元素,则直接跳过,否则就赋值给 slow 指针,并让 slow 前进一步。
复杂度
- 暴力解法
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
- 快慢指针
- 时间复杂度:O(n)
- 空间复杂度:O(1)
Code
暴力解法
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;
}
};
快慢指针
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int fast = 0, slow = 0;
while (fast < nums.size()) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
fast++;
}
return slow;
}
};
这里是先给 nums[slow] 赋值然后再给 slow++,这样可以保证 nums[0…slow-1] 是不包含值为 val 的元素的,最后的结果数组长度就是 slow。