算法训练营Day01(二分 双指针)

704. 二分查找 - 力扣(LeetCode)

关于二分查找

  • 最重要的是要处理好边界问题,每次写完边界可以带入特殊值进行测试
  • 确定区间的不变量是什么?比如区间的左闭右闭,和左闭右开,每次二分完的新区间,一定要一直和最开始的区间的定义保持一致,要时刻明确区间的含义
  • 注意二分的几种情况(当数组长度为n的情况下)
    • 当l = 0, r = n的时候因为r这个值我们在数组中无法取到,while(l < r) 是正确写法
    • 当l = 0, r = n - 1的时候因为r这个值我们在数组中可以取到,while(l <= r) 是正确写法 主要看能不能取到这个值
  • 注意处理二分mid溢出的问题
    • 当l 和 r 较大时,mid = (l + r) / 2 可能会出现大于INT_MAX的情况,会溢出。
    • 可以写成 mid = l + (r - l) / 2  或者 mid = l + ((r - l) >>1)的形式

思路:这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。

一开始的代码:

class Solution {
public:int search(vector<int>& nums, int target) {int size = nums.size();int l = 0, r = size - 1;while(l < r){int mid = (l + r) >> 1;if(nums[mid] < target)l = mid+1;elser = mid;}if(nums[r] != target)return -1;return r;}
};

这个代码可以解决数组中存在重复元素的情况,但是要注意的点有许多,第一个 l < r 当我写成 l <= r 时会死循环,因为当r = l时,进去循环,会一直r = mid 不能跳出循环,导致死循环还是要注意,这可能是不规范的写法吧。

下面有两个规范的模版(只能用于严格的升序排序,不能出现相同的元素,如果目标元素出现多次就不能正确的返回下标

左闭右闭

class Solution {
public:int search(vector<int>& nums, int target) {int size = nums.size();int l = 0, r = size - 1;while(l <= r){int mid = l + ((r - l) >> 1);if(nums[mid] < target)//mid 小于就去mid的右面去找,并且mid不为targetl = mid+1;else if(nums[mid] > target)//mid 大于就去mid的左面去找,并且mid不为targetr = mid-1;else//就是 mid = target 的情况,所以直接返回mid就可以了return mid;}//没有在循环中返回说明,没有找到目标,返回-1return -1;}
};

左闭右开

class Solution {
public:int search(vector<int>& nums, int target) {int size = nums.size();int l = 0, r = size;while(l < r)    // r不能取到,l = r 没有意义{int mid = l + ((r - l) >> 1);if(nums[mid] < target)//mid 小于就去mid的右面去找,并且mid不为targetl = mid+1;else if(nums[mid] > target)//mid 大于就去mid的左面去找,并且mid不为target,但是区间不包含mid 所以 r = midr = mid;else//就是 mid = target 的情况,所以直接返回mid就可以了return mid;}//没有在循环中返回说明,没有找到目标,返回-1return -1;}
};

其实二分还有很多应用场景,有着许多变体,比如说查找第一个大于target的元素或者第一个满足条件的元素,都是一样的,根据是否满足题目的条件来缩小答案所在的区间,这个就是二分的本质。另外需要注意,二分的使用前提:有序数组

27. 移除元素 - 力扣(LeetCode)

 我的思路(对撞指针)

        题目要求返回不等于val的个数和nums数组,我们只需要考虑返回数组前面的部分是不等于val的就可以,所以这道题的解法是双指针(对撞的),现从前面开始,如果不等于就走,等于之后 右指针在走,右指针找到不等于val的数字,与左指针指向的数字互换

class Solution {
public:int removeElement(vector<int>& nums, int val) {int l = 0, r = nums.size()-1;//如果为空直接返回0if(l > r)   return 0;while(l < r){if(nums[l] != val)l++;else{if(nums[r] != val){int tmp = nums[r];nums[r] = nums[l];nums[l] = tmp;}r--;}}//循环出来一定是 l = r 再判断 l=r时等不等于valif(nums[l] == val)  //因为返回的是个数return l ;elsereturn l+1;}
};

暴力解:

一开始要求暴力解第一时间还真没想到,后来看到题解,才想到的暴力解。

 数组中元素不能删除只能进行覆盖,所以暴力的解法就是从前往后遍历,找到val然后用后面的元素将val进行覆盖,同时将nums数组的长度减减,最后返回记录好的数组长度就可以了。

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){//val 后面的元素覆盖val都向前进行覆盖for(int j = i+1; j < size; j++){nums[j-1] = nums[j];}//交换之后,后面数组向前覆盖,不知道后面数字是不是等于val然后要再一次判断是不是等于vali--;//每覆盖一次就要size--size--;}}return size;}
};

 双指针(同向指针 \ 快慢指针)

快指针可以理解成在旧数组中找非目标元素,然后赋值给慢指针指向的新数组,虽然都指向一个数组

  • 关于二分法和移除元素的共性思考 这两题之间有点类似的,他们都是在不断缩小 left 和 right 之间的距离,每次需要判断的都是 left 和 right 之间的数是否满足特定条件。对于「移除元素」这个写法本质上还可以理解为,我们拿 right 的元素也就是右边的元素,去填补 left 元素也就是左边的元素的坑,坑就是 left 从左到右遍历过程中遇到的需要删除的数,因为题目最后说超过数组长度的右边的数可以不用理,所以其实我们的视角是以 left 为主,这样想可能更直观一点。用填补的思想的话可能会修改元素相对位置,这个也是题目所允许的。
  •  fast < nums.size()fast <= nums.size()-1 没什么区别,那为什么第二个会在空数组时报数组越界的错误? vector的size()函数返回值是无符号整数,空数组时返回了0,再减个一会溢出;变成一个很大的正数,nums[fast] 会发生 null pointer of type 'int' 异常。

双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。

定义快慢指针

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新 新数组下标的位置

所以思路就是,快指针在前面寻找不是目标元素的元素,慢指针可以理解为用来维护新数组,也就是不含目标元素的数组,如果不等于val 快慢指针都++,等于慢指针用来记录val的位置,快指针向后寻找不等于val的值与慢指针的val进行交换,直到快指针走到最后

class Solution {
public:int removeElement(vector<int>& nums, int val) {int l = 0, r = 0;//r 每一步都要走for(int i = r; i < nums.size(); i++){//可以理解为,i为一个符合条件的数组,不等于val就往里面加if(nums[i] != val)nums[l++] = nums[i];}// l 就为新数组的长度return l;}
};

 977. 有序数组的平方 - 力扣(LeetCode)

思路:看到这道题,我首先想到的是,将他们的平方都放入数组中,然后进行排序,这种时间复杂度比较高的暴力解法。后面自己想了一下可不可以使用一个时间复杂度为O(1)的方法解出来呢,一开始想用双指针,每个分别用来记录一个数组,比较然后先比较在插入,发现最后回到了插入排序的思想。

先看看暴力解法

class Solution {
public:vector<int> sortedSquares(vector<int>& nums) {for(int i = 0; i < nums.size(); i++){nums[i] = nums[i]*nums[i];}sort(nums.begin(), nums.end());return nums;}
};

 可以发现虽然通过了,但是方法还是不太好。

双指针

        我们发现有负数和正数,我们不能判断负数和正数谁的平方谁更大(为什么判断谁更大呢,因为数组是从小到大排的,负数越小平方就越大,看到这,我们可不可以先开一个一摸一样大小的数组,然后用两个指针分别记录数组两端平方较大的值呢,谁更大谁就先从后往前插入)

class Solution {
public:vector<int> sortedSquares(vector<int>& nums) {int size =nums.size();int l = 0, r = nums.size()-1;//开一个新数组vector<int>a(nums.size());//x 用来为数组下标int x = size-1;while(l <= r){//右面大,右面赋值if(nums[l]*nums[l] < nums[r]*nums[r]){a[x--] = nums[r]*nums[r];r--;//指针向左移}    else{//左面大,左面赋值a[x--] = nums[l]*nums[l];//左指针右移l++;}}return a;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/77386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

shadcn 使用步骤与注意点

目录 一、shadcn ui 二、使用流程 1.安装 2.颜色与主题 3.引用blocks 三、使用注意点 四、推荐搭配工具 五、总结 一、shadcn ui 官网&#xff1a;Build your component library - shadcn/ui 为何选择它&#xff1f;因为它是一个基于 Tailwind CSS Radix UI 的组件集…

STM32CubeMX-H7-12-IIC读写MPU6050模块(中)-MPU6050模块详解以及软件IIC驱动

前言 上一篇我们已经完成对IIC代码基本框架的编写&#xff0c;以及获取MPU6050的ID&#xff0c;接下来我们逐一分析这个模块的功能&#xff0c;并用IIC驱动 建议看完上一篇再来看这篇 MPU6050寄存器介绍 1.电源管理寄存器&#xff08;PWR_MGMT_1&#xff0c;地址&#xff1a;0…

量子计算模拟中的GPU加速:从量子门操作到Shor算法实现

一、量子模拟的算力困境与GPU破局 量子计算模拟面临‌指数级增长的资源需求‌&#xff1a;n个量子比特的态向量需要2^n个复数存储空间。当n>30时&#xff0c;单机内存已无法承载&#xff08;1TB需求&#xff09;。传统CPU模拟器&#xff08;如Qiskit的Aer&#xff09;在n28…

spring mvc 异常处理中@RestControllerAdvice 和 @ControllerAdvice 对比详解

RestControllerAdvice 和 ControllerAdvice 对比详解 1. 基本概念 注解等效组合核心作用ControllerAdviceComponent RequestMapping&#xff08;隐式&#xff09;定义全局控制器增强类&#xff0c;处理跨控制器的异常、数据绑定或全局响应逻辑。RestControllerAdviceControll…

JavaScript的回调函数:异步编程的基石

引言 在JavaScript的世界里&#xff0c;回调函数是一种强大而基础的编程模式&#xff0c;它是异步编程的核心概念之一。随着Web应用程序变得越来越复杂&#xff0c;理解和掌握回调函数变得尤为重要。本文将深入探讨JavaScript回调函数的概念、应用场景以及最佳实践。 什么是回…

测试用例 [软件测试 基础]

目录 测试用例 1. 概念 1.1 什么是测试用例 1.2 什么是要素 1.3 为什么需要测试用例 2. 设计测试用例的万能公式 2.1 常规思维 逆向思维 发散性思维 2.2 万能公式 3. 设计测试用例的方法 3.1 基于需求的设计方法 3.2 具体的设计方法 3.3 更多用例练习 测试用例 …

Jupyter notebook定制字体

一、生成配置文件 运行Anaconda Powershell Prompt终端&#xff0c;输入下面一行代码&#xff1a; jupyter notebook --generate-config 将生成文件“C:\Users\XXX\.jupyter\jupyter_notebook_config.py”&#xff0c;XXX为计算机账户名字。 二、修改配置文件 c.NotebookAp…

miniconda安装R语言图文教程(详细步骤)

本篇教程介绍,如何在Windows使用miniconda安装R语言。 一、创建1个conda 虚拟环境 # 创建虚拟环境 conda create -n r_env # 激活虚拟环境 conda activate r_env二、安装 R 语言 conda install -c r r-ggplot2三、运行测试 检查安装: 输入 R 进入 R 的交互式命令行,检查是…

【day1】AI软件测试学习笔记

以下为整理的 AI软件测试学习笔记&#xff0c;涵盖性能测试工具链、AI大模型应用及开发实践&#xff0c;分为四大模块&#xff1a; 一、性能测试工具链与数据分析 1. 工具链整合效果 JMeter InfluxDB Grafana JMeter压测数据存储至云端InfluxDB&#xff0c;实现分布式压测和…

WPF 资源加载问题:真是 XAML 的锅吗?

你的观察很敏锐&#xff01;确实&#xff0c;在 WPF 项目中&#xff0c;.cs 文件主要负责逻辑实现&#xff0c;而资源加载的问题通常跟 XAML&#xff08;以及它背后的 .csproj 配置&#xff09;关系更大。我会围绕这个观点&#xff0c;用 CSDN 博客风格详细解释一下 .cs、XAML …

C++17模板编程与if constexpr深度解析

一、原理深化 1.1 模板编程 1.1.1 编译器如何处理模板&#xff08;补充&#xff09; 模板的实例化机制存在两种模式&#xff1a; 隐式实例化&#xff1a;编译器在遇到模板具体使用时自动生成代码&#xff0c;可能导致多翻译单元重复实例化&#xff0c;增加编译时间。显式实…

408 计算机网络 知识点记忆(6)

前言 本文基于王道考研课程与湖科大计算机网络课程教学内容&#xff0c;系统梳理核心知识记忆点和框架&#xff0c;既为个人复习沉淀思考&#xff0c;亦希望能与同行者互助共进。&#xff08;PS&#xff1a;后续将持续迭代优化细节&#xff09; 往期内容 408 计算机网络 知识…

MySQL学习笔记十四

第十六章创建高级联结 16.1使用表别名 输入&#xff1a; SELECT CONCAT(vend_name,(,RTRIM(vend_country),)) AS vend_title FROM vendors ORDER BY vend_name; 输出&#xff1a; 输入&#xff1a; SELECT cust_name, cust_contact FROM customers AS c, orders AS o, or…

Spring MVC 框架 的核心概念、组件关系及流程的详细说明,并附表格总结

以下是 Spring MVC 框架 的核心概念、组件关系及流程的详细说明&#xff0c;并附表格总结&#xff1a; 1. 核心理念 Spring MVC 是基于 MVC&#xff08;Model-View-Controller&#xff09;设计模式 的 Web 框架&#xff0c;其核心思想是 解耦&#xff1a; Model&#xff1a;数…

Android里蓝牙使用流程以及问题详解

一、基础流程 请简述 Android 蓝牙开发的基本流程 1. 权限处理&#xff1a;动态申请蓝牙和定位权限&#xff08;注意Android 12新权限&#xff09; 2. 初始化蓝牙适配器&#xff1a;通过BluetoothManager获取BluetoothAdapter 3. 设备发现&#xff1a;- 注册BroadcastReceive…

OpenWrt 上安装Tailscale

在 OpenWrt 上安装 Tailscale 非常简单&#xff0c;主要步骤如下&#xff1a; 1. 确保 OpenWrt 设备可联网 首先&#xff0c;确保你的 OpenWrt 设备已经联网&#xff0c;可以访问外网&#xff0c;并且 SSH 进入你的路由器&#xff08;通常是 192.168.1.1&#xff09;&#xff…

蓝桥杯刷题总结 + 应赛技巧

当各位小伙伴们看到这篇文章的时候想必蓝桥杯也快开赛了&#xff0c;那么本篇文章博主就来总结一下一些蓝桥杯的应赛技巧&#xff0c;那么依旧先来走个流程 那么接下来我们分成几个板块进行总结 首先是一些基本语法 编程语言的基本语法 首先是数组&#xff0c;在存数据的时候…

TCP重传率高与传输延迟问题

目录标题 排查步骤&#xff1a;TCP重传率高与传输延迟问题v1.0通过 rate(node_netstat_Tcp_RetransSegs[3m]) 排查 TCP 重传问题的步骤1. **指标含义与初步分析**2. **关联指标排查**3. **定位具体问题源**4. **解决方案**5. **验证与监控** v2.0一、基础检查二、网络层分析三、…

【LeetCode 热题100】73:矩阵置零(详细解析)(Go语言版)

&#x1f680; 力扣热题 73&#xff1a;矩阵置零&#xff08;详解 多种解法&#xff09; &#x1f4cc; 题目描述 给定一个 m x n 的整数矩阵 matrix&#xff0c;如果一个元素为 0&#xff0c;则将其所在行和列的所有元素都设为 0。请你 原地 使用常量空间解决。 &#x1f3a…

组播网络构建:IGMP、PIM 原理及应用实践

IP组播基础 组播基本架构 组播IP地址 一个组播IP地址并不是表示具体的某台主机&#xff0c;而是一组主机的集合&#xff0c;主机声明加入某组播组即标识自己需要接收目的地址为该组播地址的数据IP组播常见模型分为ASM模型和SSM模型ASM&#xff1a;成员接收任意源组播数据&…