代码随想录算法训练营第一天 | ( Part 1 ) 704. 二分查找
代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素
704. 二分查找
题目链接:https://leetcode.cn/problems/binary-search/
文档链接:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html (源于代码随想录)
可以参考视频讲解:https://www.bilibili.com/video/BV1fA4y1o715 (源于代码随想录)
思路:
处理对象 为 数组 , 且 有序 ,
数组 可以 随机访问 , 访问 某一确认位置 的 时间复杂度 为 O(1)
对于 有序 的 线性表 ,与 中间 的 值 进行 比较,一次 可以 排除掉 一半的数据 (/直接 找到 目标)
相对于在数组 中 一个一个 找 效率 有了 很大 的 提升
整体查找 的 时间复杂度 为 log(n)
易错点 和 需要注意的点:
-
target 和 mid点 在 写代码时 容易 下意识 搞混了
-
选择左侧区间时 注意 调整右边界 , 选择右侧区间时 注意 调整左边界
-
注意 不要忘了 更新 mid 点 的值
实现时 的 思路:
-
使用 C++ STL标准库 中 的 begin() 、end()、获取 相对于 数据类型 的 逻辑 下标 , 进而获取数组的长度
-
根据 比较 的 情况( 等于 / 小于 / 大于 ) ,提交 结果 下标 / 选择 左 右区间 ,
(如果 思考 相对 负载 太大 , 可以 将 内容 划分 , 先 从小块的内容 开始 思考/处理 ,逐渐 削弱 ,/ “蚕食” 问题 )
-
在 求 mid 值 时 ,注意 防溢出 , “使用减法 配合 ,削减 中间结果 可能出现 的 最大值”
mid = left + ( right - left ) / 2 ;
-
“递归 逻辑” 运行 到 left 或 right 越过 对方 后
(实际上 是 非递归 实现)
-
当 left == right 时 , 还有 最后 一个 元素 进行 比较 (在 虚拟渲染 运行 过程 时 可以 先从 一些 特殊/特征 点 思考 起 )
代码 实现:
int search(vector<int>& nums, int target) {
int len = end(nums) - begin(nums);
int left = 0;
int right = (len - 1);
int mid = left + (right - left)/2; //int mid = (left + right)/2;
while(left<=right){
if(target==nums[mid])
{
return mid;
}else
if(target<nums[mid]) //target 、 mums[mid] 在思考时 易 混晞
{
right = mid - 1; //注意 “选左区间 改 右边界 ,选右区间 改 左边界 ”
}else
if(target>nums[mid])
{
left = mid + 1;
}
mid = left + (right - left)/2; // 易漏
}
return -1;
}
27. 移除元素
题目链接:https://leetcode.cn/problems/remove-element/
文档链接:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html(源于代码随想录)
可以参考视频讲解:https://www.bilibili.com/video/BV12A4y1Z7LP (源于代码随想录)
思路:
基础实现思路: (这次是暴力解)
顺着数组进行扫描,将所有符合要求的元素删除,(删除 操作 : 将 后边 的 有效元素 前移 (1 个 位置))
将 “删除 操作” 提取 分离 ,单独 进行 实现 , 以 简化 程序 结构
暴力解:
int removeElement(vector<int>& nums, int val) {
int len = end(nums) - begin(nums);
int count = 0;
int i = 0;
//注意 预防 溢出
//while((i<(len-count))&&(i<len))
//分情况 分析 如 // 在更早循环删除的 / 在前一次循环删除的
while((i<(len-count)))
{
//重要 , 关键 的 内循环
if(nums[i] == val)
{
moveLeft_1_FromRange(nums, (i+1) , (len - 1 - count));
count++;
}
else //xx 位置 的 信息 更新了 , 需要 重新 判断
{
i++;
}
}
return (len - count);
}
void moveLeft_1_FromRange(vector<int>& nums, int left , int right)
{
int i = left - 1;
for(;i < right ; i++)
{
nums[i] = nums[i+1];
}
}
思路 —— 双指针 法:
双指针法,简单来说,就是 准备 一个指针,来将 整个 数组 的 元素 进行扫描,然后再准备一个指针 来接收 要存留下来的元素。
这次 数组 修改的频率 比较高,时间量级 到了 O(n)
但其实如果你 不这么做, 按照 ( 将 删除元素 后边 的 元素 全部 前移 ) 的 方法 进行 , 修改 的 频率 更高 , 能 达到 O(n²)
这里 是 “有舍才有得” ,“用 ‘记录 需要保留 元素’ 的 代价 ,简化 元素 位置 的 调整,来换取 整个操作 修改频率 的 降低”
可以为 每一个 分支任务 分配一个 指针 进行 支持,以使 我们的程序 更具有 操作性 ,
为 程序 以后的 发展 奠基 , 使 长远 的 发展 成为 可能
双指针 法:
int removeElement(vector<int>& nums, int val) {
int len = end(nums) - begin(nums);
int count = 0;
int j = 0;
int i = 0;
// 易 写 成 while(j < (len - count)) //这回 j 指针 要 “运行 到 头”
while(j < len )
{
if(nums[j]==val)
{
j++;
count++;
}
else // nums[j]!=val
{
nums[i] = nums[j];
i++;
j++;
//修改 元素 的 频率 有点 高 , 但 如果 对 要删除的元素 后边 的 元素 进行 依次 顺移 , 修改 的 频率/量 更高
}
}
return (len - count);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)