代码随想录算法训练营第一天 | LeetCode704.二分查找、LeetCode27.移除元素
704.二分查找
题目链接:704.二分查找
思路:
- 设置两个逻辑指针,指向数组的头尾两个元素;
- 判断它们中间的值和目标元素的关系:
- 若目标元素更大,则在这个中间值的左侧子数组中,若小则相反,若相等则返回当前地址;
- 若前后指针指向同一位置时还未找到则返回不存在
时间复杂度:O(logn)
,空间复杂度O(1)
代码:
class Solution
{
public:
// 左闭右闭
int search(vector<int> &nums, int target)
{
int front = 0;
int back = nums.size() - 1;
while (front <= back)
{
int mid = front + (back - front) / 2;
if (nums[mid] < target)
{
front = mid + 1;
}
else if (nums[mid] == target)
{
return mid;
}
else
{
back = mid -1;
}
}
// 只要数组内有target元素,必定会在while循环中找到并return
return -1;
}
};
总结:
- 二分查找基本练习,算是热身,后面maybe有一些题需要用到这个思路,最好能达到快速默出来
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
27.移除元素
题目链接:27.移除元素
思路废话:
- 这题我的思路和代码随想录的文章中有所出入:
- 要求:将值为val的数移除,原地修改
- 若数组有n个数,val值由r个,处理过后不为val的n-r个值都要放在数组前n-r的位置上
- 我就想到了用交换位置的想法,将值为val的数都扔到数组后侧。
思路:
- 用一个双指针,指向数组头尾(和上面一题很像哦)
- 头指针负责判断当前元素是否为val,如果是则交换
- 而尾指针负责记录接下来应该交换的数组尾的位置,每交换一次就往前移一位
- 头尾指针相遇则结束,相遇的位置就是移动完之后第一个val的的位置,也代表了所求数组的长度
- (更多细节看代码注释)
时间复杂度:O(n)
,空间复杂度O(1)
代码:
class Solution
{
public:
int removeElement(vector<int> &nums, int val)
{
int front = 0;
int back = nums.size() - 1;
while (front < back)
{
if (nums[front] == val)
{
// 有可能从数组尾换回来的还是一个val
// 头指针留在原地下一轮再判断一次
swap(nums[front], nums[back]);
--back;
}
else
{
++front;
}
}
if (nums.size() != 0) // 如果数组本身为空,使用索引会越界
{
// 当头尾指针相遇的时候,如果指向的元素不是val
// 则还要向后退一位指向第一个val
if (nums[front] != val)
++front;
}
return front;
}
void swap(int &a, int &b)
{
int tep = a;
a = b;
b = tep;
}
};
总结:
- 这题昨天刚做过,今天几乎直接就默出来了,下午在上班,没来的及理解一下carl哥的思路[TODO],留作周末作业吧
- 做题体验:
嘿嘿
- 时间:
未计
二刷
34.在排序数组中查找元素的第一个和最后一个位置[补充]
思路:
- 以704的基本框架为基础
- 找到任意一个target的位置
- 然后向前,向后遍历找到第一次和最后一次出现的位置
时间复杂度:O(n)
,空间复杂度O(1)
代码:
class Solution
{
public:
vector<int> searchRange(vector<int> &nums, int target)
{
std::vector<int> res = {-1, -1};
auto length = nums.size();
if (length == 0)
return res;
int front = 0;
int back = length - 1;
int mid;
bool hasTarget = false;
while (front <= back)
{
mid = front + (back - front) / 2;
if (nums[mid] < target)
{
front = mid + 1;
}
else if (nums[mid] == target)
{
hasTarget = true;
break;
}
else
{
back = mid - 1;
}
}
if (!hasTarget)
return res;
res[0] = mid;
res[1] = mid;
for (int i = mid - 1; i >= 0; i--)
{
if (nums[i] != target)
{
res[0] = i + 1;
break;
}
else
{
res[0] = i;
}
}
for (int i = mid +1; i < length; i++)
{
if (nums[i] != target)
{
res[1] = i-1;
break;
}
else
{
res[1] = i;
}
}
return res;
}
};
总结:
- 这题做得太拉跨咯,写完博客看一下好的题解吧[TODO],这样才算做完
- 原本是想找到任意个target之后,对当前位置的左右两个子数组再进行一次二分查找,找到非target值和target值的分界线,这样算法的时间复杂度是
3longn
,也属于O(logn)
级别的,但临界情况和数组越界问题调了一个小时也没调通。发现LeetCode上的测试案例的数组长度并不大,改用遍历 - 做题体验:
嘤嘤
- 时间:
未计
一刷
35.搜索插入位置[补充]
题目链接:35.搜索插入位置
思路:
- 以704的基本框架为基础
- 遍历之后若没找到元素,则插在当前头指针位置
时间复杂度:O(n)
,空间复杂度O(1)
代码:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int front = 0;
int back = nums.size()-1;
while(front <= back)
{
int mid = front + (back - front) / 2;
if(nums[mid] < target)
{
front = mid +1;
}
else if(nums[mid] == target)
{
return mid;
}
else
{
back = mid -1;
}
}
return front;
}
};
总结:
- 第一次刷的时候对跳出while循环之后,直接返回front不是太理解,写的有点复杂
- 其实跳出循环只有两种情况:、
nums[mid] < target
时front = mid + 1
导致front <= back
跳出循环。
当前front索引位置的值大于target,前一个位置小于target,所以应该返回当前的front。另一种情况比较好理解不说了。
- 做题体验:
嘿嘿
- 时间:
未计
二刷
,昨天刚跟按照公众号文章做过这一题