题目链接:704、二分查找
文章链接
状态:最后返回-1的情况没弄好。
思路:首先审题是升序,有序数组,要用二分查找。二分查找主要分为四种:左闭右闭[用的最多]、左闭右开[其次]、左开右闭和左开右开用的都很少。因此主要写前两种的代码。
第一种左闭右闭
lass Solution {
public:
int search(vector<int>& nums, int target) {
int size = nums.size();
int left = 0;
int right = size - 1;
while (left <= right)
{
int mid = (left + right) >> 1;
if (nums[mid] > target) right = mid - 1;
else if (nums[mid] < target) left = mid + 1;
else return mid;
}
return -1;
}
};
第二种 左闭右开
class Solution {
public:
int search(vector<int>& nums, int target) {
int size = nums.size();
int left = 0;
int right = size - 1;
while (left < right)
{
int mid = (left + right) >> 1;
if (nums[mid] > target) right = mid;
else if (nums[mid] < target) left = mid + 1;
else return mid;
}
return -1;
}
};
题目链接:27.移除元素
文章链接
状态:暴力解法的i–和size–放错了括号,双指针解法没有问题。
思路:看到题目的第一想法就是用两个循环暴力解出来,但是当测试数据特别多的时候,时间复杂度会爆表,在工作时会很浪费算力,但是在考试上机时实在想不出来暴力算法还是可以拿分的。双指针解法是跟卡哥学的,很好用。就是有时候想不起来用双指针,还是要多加练习,这次的双指针代码自己写出来了。
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;
}
};
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
int slownode = 0;
for (int fastnode = 0; fastnode < size; fastnode++)
{
if (nums[fastnode] != val)
{
nums[slownode++] = nums[fastnode];
}
}
return slownode;
}
};
题目链接
文章链接没有,就看我做的吧。
状态:知道是用二分法但是想不出来该怎么写。
思路:一直在想二分法但是没想出来,看题解看懂了,但是中间有段卡住了,主要讲一下为什么卡住。
我在纸上举了一个例子[1,3,5,6,7,8,9] 令target = 2。一开始l = 0,r = 6;所以mid = 3,此时num[mid] = 6 > 2,此时r = 2.所以mid = 1。此时num[mid] = 3 > 2,
所以r = 0。我一直以为到这里就结束了,返回值 l 是0,也不会是该插入的下标啊,在这里我思考了很久,我又看了一遍题解,想到了while的循环还没有完,还需要再进行一次,此时l = 0, r = 0,mid = 0,所以num[0]= 1 < 2,所以l = mid + 1 = 1,此时l > r 不满足循环 退出 返回 l,此时 l = 1,满足题意,我豁然开朗。我参照的题解在下面。
题解
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int size = nums.size();
int l = 0;
int r = size - 1;
while (l <= r)
{
int mid = (l + r) >> 1;
if (nums[mid] < target) l = mid + 1;
else r = mid - 1;
}
return l;
}
};
添加上一位老哥的解释:最后直接返回left就可以了,根据if的判断条件,left左边的值一直保持小于target,right右边的值一直保持大于等于target,而且left最终一定等于right+1,这么一来,循环结束后,在left和right之间画一条竖线,恰好可以把数组分为两部分:left左边的部分和right右边的部分,而且left左边的部分全部小于target,并以right结尾;right右边的部分全部大于等于target,并以left为首。所以最终答案一定在left的位置。
还有一种开区间的写法,我理解深刻的。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int size = nums.size();
int l = 0;
int r = size;
while (l < r)
{
int mid = (l + r) >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
return l;
}
};
这里是背的y总的模板,在数组最后加入元素时r = size,此时循环判断里是r = mid的情况,如果是l = mid 只能是r = size - 1。
题目链接:34.在排序数组中查找元素的第一个和最后一个的位置
状态:大体的代码可以写出来,但是运行不出来。
思路:用两次二分法,分别找到大于等于的边界和小于等于的边界。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int size = nums.size();
int l = 0;
int r = size - 1;
int start,end = 0;
if (size == 0) return {-1,-1};
while (l < r)
{
int mid = (l + r) / 2;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
start = l;
if(nums[l] != target) return {-1,-1};
else
{
int l = 0;
int r = size - 1;
while (l < r)
{
int mid = (l + r + 1) / 2;
if (nums[mid] <= target) l = mid;
else r = mid - 1;
}
end = l;
}
return {start,end};
}
};
这是自己写的代码,但是运行不出来,自己看不出来什么问题,先这样,晚上ac出来再改。
问题解决了没有考虑nums数组为空的时候,加了一条判断完美解决。