本文章代码以c++为例!
一、力扣第704题:二分查找
二分查找在学校的课程里面有学过,然后在看了一下代码随想路在b站的视频,更深刻的理解二分查找,知道了二分查找有两种写法,左闭右闭即[left, right],或者左闭右开即[left, right)。
我写的是第一种方法,左闭右闭即[left, right]。
区间的定义这就决定了二分法的代码应该如何写,因为我定义target在[left, right]区间,所以有如下两点:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
int low = 0, high = nums.size() - 1;
while(low <= high){
int mid = (high - low) / 2 + low;
int num = nums[mid];
if (num == target) {
return mid;
} else if (num > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
};
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
二、力扣第27题:原地移除元素
1.思路一:
其实对于这道题而言,我一开始最先想到的方法是直接两层for循环暴力解法,一个for循环遍历数组元素 ,第二个for循环更新数组。暴力解法的时间复杂度是O(n^2),这道题目暴力解法在leetcode上是可以过的。
代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int s = nums.size();
for (int i = 0; i < s; i++) {
if (nums[i] == val) {
for (int j = i + 1; j < s; j++) {
nums[j - 1] = nums[j];
}
i--; s--;
}
}
return s;
}
};
后来看了代码随想录的b站视频,知道了还有第二种解法,用双指针法(快慢指针法)来解,这样子大大降低了时间复杂度,从O(n^2)变成了O(n),双指针是是算法中的一种技巧,双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。最常见的双指针算法有两种:一种是,在一个序列里边,用两个指针维护一段区间;另一种是,在两个序列里边,一个指针指向其中一个序列,另外一个指针指向另外一个序列,来维护某种次序。
在视频里卡哥解释了如何利用双指针法来解决这道题,通过一个快指针和慢指针在一个for循环下来完成两个for循环的工作。
代码如下:(有解析)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
// 找左边等于val的元素
while (left <= right && nums[left] != val){
++left;
}
// 找右边不等于val的元素
while (left <= right && nums[right] == val) {
-- right;
}
// 将右边不等于val的元素覆盖左边等于val的元素
if (left < right) {
nums[left++] = nums[right--];
}
}
return left; // leftIndex一定指向了最终数组末尾的下一个元素
}
};
注意这些实现方法并没有改变元素的相对位置!
- 时间复杂度:O(n)
- 空间复杂度:O(1)
打卡day 1