题目链接:704. 二分查找 & 27. 移除元素
今日学习的文章链接704. 二分查找 & 27. 移除元素和视频链接704. 二分查找 & 27.移除元素
目录
704. 二分查找
自己看到题目的第一想法
二分查找,因为nums数组是升序,所以每次只需要比较下标的一半的值与target的大小关系。
因为是vector容器,所以可以使用vector_name.size()
获得容器内元素个数,并且支持[]
操作符获取指定元素。这样可以使用下标来进行取值和判断,比使用迭代器要方便的多。
需要手动模拟三个变量head tail middle
在两种情况下的变化,从第二种情况得出循环的判定条件是head <= tail
。两个示例已覆盖边界条件,无需其他判定。
class Solution {
public:
int search(vector<int>& nums, int target) {
int middle = 0, head = 0, tail = nums.size() - 1;
while (head <= tail) {
middle = (head + tail) / 2;
if (target == nums[middle]) return middle;
else if (target > nums[middle]) head = middle + 1;
else tail = middle - 1;
}
return -1;
}
};
看完代码随想录之后的想法
除了升序还需要注意无重复元素,否则返回的下标不是唯一的。此题难在边界条件确认,也就是循环不变量,每个循环开始之前,边界要确认下来,一般来讲常用两种情况:左闭右闭或左闭右开,循环不变量的意思就是在每次二分过程中,区间都要遵循自己的规定。
时复:O(logn),因为每次取一半。
自己实现过程中遇到哪些困难
时复一开始没有写。
今日收获,记录一下自己的学习时长
学到了左闭右开区间的写法,两个写法相同点在于坚持循环不变量。
27. 移除元素
自己看到题目的第一想法
题干很长,有点儿唬人,看了半天,直接开写。暴力解法:新建一个vector数组,把不等于val的元素放进去,nums指向新的vector数组。
// 需要额外的数组,空间复杂度 O(n),时间复杂度 O(n)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int k = 0;
vector<int> expectedNums;
for (auto i = nums.begin(); i < nums.cend(); i++) {
if (*i != val) expectedNums.push_back(*i), k++;
}
nums = expectedNums;
return k;
}
};
看完代码随想录之后的想法
题目要求原地操作,所以不可用使用另外的数组。原地暴力解法:
// 时间复杂度 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--;
size--;
}
}
return size;
}
};
快慢双指针解法:
思路在于数组的“删除”并不是真正的删除,而是更新也可以叫做覆盖。一个快指针,一个慢指针,快-寻找,慢-更新,更新是主要操作,因为是需要一个不含val的新数组,所以碰到不等于val的就进行更新。
// 时间复杂度 O(n),空间复杂度 O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0, fast = 0;
for (; fast < nums.size(); fast++) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
};
可以所代码简化,但是只是等价变形,并且不利于后来复习。
自己实现过程中遇到哪些困难
双指针解法需要自己模拟一遍,因为需要在原数组上进行更新覆盖,所以看起来/理解起来有点儿复杂。
最后返回slow
就是新数组的个数,因为fast
作为寻找新元素的快指针已经到循环终止条件了,不会再有新的元素,同时slow
此时已经是指向下一个待判断元素。
今日收获,记录一下自己的学习时长
快慢双指针算法。
曾经的解法
1、卡题目条件,50是数组元素的最大值。用到库函数sort()
。
// 时间复杂度 O(nlogn),空间复杂度 O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int cnt = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] == val) {
nums[i] = 51;
cnt++;
}
}
sort(nums.begin(), nums.end());
// for (int i : nums) {
// cout << i << " ";
// }
return nums.size() - cnt;
}
};
2、前后双指针解法:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//特判:全部元素=val
int flag = 0, cnt = 0;
for (int i : nums) {
if (i != val) flag = 1;
else cnt++;
}
if (flag == 0) return 0;
int i = 0, j = nums.size() - 1, tmp = 51;
while (i < j) {
if (nums[i] != val && i < j) {
i++;
}
if (nums[j] == val && i < j) {
j--;
}
tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
//cout << nums.size() << cnt;
return nums.size() - cnt;
}
};