【算法-数组1】二分查找 和 移除元素

今天,带来XXX的讲解。文中不足错漏之处望请斧正!

理论基础


二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

1. 思路

有序、不重复的数组,是个很让人嘴馋的条件,它天然地有很大优势。

听过猜数游戏吗?猜1~100中的目标数,有最快的方法:每次把答案所处的范围缩小一半。

比如要猜78:

当前选数: 50

50小了,接下来的范围是:[50, 99]

当前选数: 75

75小了,接下来的范围是:[75, 99]

当前选数: 88

88大了,接下来的范围是:[75, 86]

当前选数: 81

81大了,接下来的范围是:[75, 79]

当前选数: 78

猜对了,78就是答案

每次,范围都能缩小一半,用当前选数来把整个范围一分为二。

这其实就叫二分查找算法:对于有序不重复的数组,每次取整个区间中间位置的值,判断这个值和目标谁大,中间值大,说明目标在左边,反之说明目标在右边。

在这里插入图片描述

2. 参考代码

2.0 循环不变量

很多朋友写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
一般来说,left 和 right 有左闭右闭和左闭右开两种定义。

class Solution {
public:int search(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while(left ? right) {int mid = left + (right - left) / 2;if(target < nums[mid])  right = ?;if(nums[mid] < target)  left = ?;if(target == nums[mid]) return mid;}return -1;}
};
细节1:while如何判断

while判断是<还是≤?

细节2:如何缩范围

确定了nums[mid]和target的关系,我们要怎么缩范围呢?这里难道不可以right = mid; left = mid;吗?

根据循环不变量确定细节

首先要根据题意来明确,我们搜索(查找)的区间是左闭右开还是左闭右闭。

  1. while判断:区间的定义
    1. 左闭右闭:while(left ≤ right),因为left==right的时候,没有把整个数组搜索完,且[left,right]是合法区间
    2. 左闭右开:while(left < right),因为left==right的时候,整个数组已经搜索完了,且[left,right)不是合法区间
  2. 缩范围:区间的定义 + 要缩范围时mid位置是否可被排除
    1. 左闭右闭+mid位置可被排除:right = mid-1; left = mid+1
    2. 左闭右开+mid位置可被排除:right = mid; left = mid+1
    3. 左闭右闭+mid位置不可被排除:right = mid; left = mid
    4. 左闭右开+mid位置不可被排除:right = mid+1; left = mid

2.1 左闭右闭版

所以回过头分析左闭右闭的写法:

  • 当left==right,[left,right]是合法区间——while(left ≤ right)
  • 左闭右闭+mid位置可被排除——right= mid - 1; left = mid + 1;
class Solution {
public:int search(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while(left <= right) {int mid = left + (right - left) / 2;if(target < nums[mid])  right = mid - 1;if(nums[mid] < target)  left = mid + 1;if(target == nums[mid]) return mid;}return -1;}
};

2.2 左闭右开版

class Solution {
public:int search(vector<int>& nums, int target) {int left = 0, right = nums.size();while(left < right) {int mid = left + (right - left) / 2;if(target < nums[mid])  right = mid;if(nums[mid] < target)  left = mid + 1;if(target == nums[mid]) return mid;}return -1;}
};
  • 当left==right,[left,right)是非法区间,如[1, 1)是非法的,不可能既包含1又不包含1——while(left < right)
  • 左闭右开+mid位置可被排除:right = mid; left = mid+1

相关题目推荐

  • 35.搜索插入位置(opens new window)
  • 34.在排序数组中查找元素的第一个和最后一个位置(opens new window)
  • 69.x 的平方根
  • 367.有效的完全平方数

移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

1. 思路

1.1 暴力

两层循环,找到val就执行覆盖操作。

1.2 替换法

如果不关心顺序,可以用替换法,若当前位置等于val,把最后一个位置赋给当前位置,删除最后一个位置(尾删是O(1))。

1.3 双指针法

快指针遍历数组,找不需要移除的元素赋给慢指针。慢指针维护的是不需要移除的元素。

在这里插入图片描述
*图片来自代码随想录

2. 参考代码

2.1 暴力

class Solution {
public:int removeElement(vector<int>& nums, int val) {size_t i = 0;while(i < nums.size()) {if(nums[i] == val) {int cur = i, next = i + 1;while(next < nums.size()) nums[cur++] = nums[next++]; //最后一个不用管了nums.pop_back();}if(nums[i] != val) ++i; //覆盖后如果不判断就直接++i,可能跳过要移除的元素}return nums.size();}
};

O(N^2)的时间复杂度,比较弱。

2.2 替换

class Solution2 {
public:int removeElement(vector<int>& nums, int val) {//不考虑元素顺序:替换法删除size_t i = 0;while(i < nums.size()) {if(nums[i] == val) {while(!nums.empty() && nums.back() == val) nums.pop_back();if(i < nums.size()) {nums[i] = nums.back();nums.pop_back();}}++i;}return nums.size();}
};

O(N)的时间复杂度。

2.3 双指针

class Solution {
public:int removeElement(vector<int>& nums, int val) {int slow = 0;for(size_t fast = 0; fast < nums.size(); ++fast) {if(nums[fast] != val) nums[slow++] = nums[fast];}return slow;}
};

O(N)的时间复杂度。


今天的分享就到这里了,感谢您能看到这里。

这里是培根的blog,期待与你共同进步!

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

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

相关文章

yum 命令

基本语法 yum [选项] [参数] 选项说明 -y 对所有提问都回答“yes” 参数说明 实操 yum list | grep firefox yum -y remove firefox yum -y install firefox

使用MobaXterm向linux窗口化传输文件

使用MobaXterm向linux窗口化传输文件 之前上大学的时候&#xff0c;经常是XSheel配合Xftp使用&#xff0c;Xftp可以窗口化的往linux服务器传输文件&#xff0c;但是有一个问题&#xff0c;就是Xftp是收费的。 后来工作之后师兄给推荐了一个免费的&#xff0c;又好用的类似于Xf…

uni-app/vue 文字转语音朗读(附小程序语音识别和朗读)uniapp小程序使用文字转语音播报类似支付宝收款播报小程序语音识别和朗读)

uni-app/vue 文字转语音朗读&#xff08;小程序语音识别和朗读&#xff09; uniapp小程序功能集合 1、uniapp小程序文字转语音播报 一、第一种方式&#xff1a;直接加语音包 固定的文本 先利用工具生成了 文本语音mp3文件&#xff0c;放入项目中&#xff0c;直接用就好了 …

【CSS】position

CSS position 1.静态布局 static static 是 position 属性的默认值&#xff0c;表示没有定位。使用静态定位的元素会按照元素正常的位置显示&#xff0c;并且不会受到 top、bottom、left、right 和 z-index 属性的影响。 2.相对定位 relative 相对定位就是元素相对于自己默…

Spring Cloud 之RabbitMQ的学习【详细】

服务通信 分布式系统通信两种方式&#xff1a; 直接远程调用&#xff08;同步&#xff09;借助第三方间接通信&#xff08;异步&#xff09; 同步通讯的问题 Feign就属于同步通讯。存在的如下问题 耦合度高&#xff0c;每次添加新的模块就要修改原有模块的代码性能下降&am…

css(层叠样式表)

文章目录 一、CSS介绍二、CSS使用方式1. 行内样式/内联样式&#xff08;单一页面中使用&#xff09;设置背景颜色 background-color:green; 2. 内嵌样式&#xff08;少量页面中使用&#xff09;3. 外链样式表&#xff08;项目中使用&#xff09; 三、 样式表特征1. 层叠性2. 继…

学习redis之前的泛泛而谈(特性介绍,应用场景,Ubuntu安装与通用命令介绍)

文章目录 前言关于分布式系统Redis特性Redis应用场景Redis5安装redis命令最核心的两个命令&#xff1a;get和setkeysexitsdelexpirettlredis中key的过期策略type redis数据类型的内部实现方式redis的单线程 前言 redis最重要的概念&#xff1a;在内存中存储数据 为什么要设计一…

代购商城源码是否可以定制开发?

定制开发&#xff0c;符合个性需求 代购商城源码是现代电子商务中的重要工具&#xff0c;它为代购商提供了建立在线店铺、管理产品和订单、处理支付和物流等功能。然而&#xff0c;对于不同的代购商而言&#xff0c;在源码的基础上进行个性化定制开发无疑是提升竞争力和用户体验…

2023年软件测试工具总结 —— 单元测试工具

在应用程序中&#xff0c;单元是具有一个或多个输入和单个输出的软件中最小可测试部分。单元测试是一种测试软件代码单元的方法&#xff0c;通常包括一个或两个输入&#xff0c;产生一个输出。单元测试主要关注独立模块的功能正确性&#xff0c;目的是确保每个单元都按照预期的…

毛发渲染方案实现

一、毛发材质概述 以前毛发只能用离线来做 现在实时毛发逐渐可能。长毛渲染和短毛渲染采用的是不同的方案。 二、长毛类制作分析 各向异性 kajiya算法 # 三、短毛类制作分析 四、制作心得及技巧

成为一个优秀的测试工程师需要具备哪些知识和经验?

看到这个题目&#xff0c;头脑中马上就拆分出了3个小问题&#xff1a; 1、什么是优秀的测试工程师&#xff1f; 2、优秀测试工程师需要哪些知识&#xff1f; 3、优秀测试工程师需要哪些经验&#xff1f; 一个个讲解。 一、什么才是一名优秀的测试工程师呢&#xff1f; 什么才是…

奇富科技引领大数据调度革命:高效、稳定、实时诊断

日前&#xff0c;在世界最大的开源基金会 Apache旗下最为活跃的项目之一DolphinScheduler组织的分享活动上&#xff0c;奇富科技的数据平台专家刘坤元应邀为国内外技术工作者献上一场题为《Apache DolphinScheduler在奇富科技的优化实践》的精彩分享&#xff0c;为大数据任务调…

C++进阶语法——智能指针【学习笔记(五)】

文章目录 1、智能指针简介1.1 原始指针&#xff08;raw pointer&#xff09;的⼀些问题1.2 智能指针&#xff08;smart pointers&#xff09; 2、智能指针&#xff08;smart pointers&#xff09;——unique_ptr2.1 unique_ptr 的声明2.2 unique_ptr 的函数2.3 ⾃定义类型使⽤ …

Go-Python-Java-C-LeetCode高分解法-第十二周合集

前言 本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接&#xff1a;LeetCode-Go-Python-Java-C 欢迎订阅CSDN专栏&#xff0c;每日一题&#xff0c;和博主一起进步 LeetCode专栏 我搜集到了50道精选题&#xff0c;适合速成概览大部分常用算法 突…

比较Excel中的两列目录编号是否一致

使用java代码比较excel中两列是否有包含关系&#xff0c;若有包含关系&#xff0c;核对编号是否一致。 excel数据样例如下&#xff1a; package com.itownet.hg;import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import j…

C++设计模式_21_Iterator 迭代器(理解;面向对象的迭代器已过时;C++中使用泛型编程的方式实现)

Iterator 迭代器也是属于“数据结构”模式。GoF中面向对象的迭代器已经过时&#xff0c;C中目前使用泛型编程的方式实现&#xff0c;其他语言还在使用面向对象的迭代器。 文章目录 1. 动机(Motivation)2. 模式定义3. Iterator 迭代器代码分析4. 面向对象的迭代器与泛型编程实现…

基于MFC的串口通信(Mscomm)

1、串口通信的概述&#xff1a; 串口是一种重要的通信资源&#xff0c;例如鼠标口、USB接口都是串口。串行端口是CPU和串行设备间的编码转换器。当数据从CPU经过端口发送出去的时候&#xff0c;字节数据会被转为串行的位&#xff0c;在接收数据时&#xff0c;串行的位被转换为…

用Visual Studio(VS)开发UNIX/Linux项目

目录 FTP是免不了的 正确设置头文件 组织项目结构 创建何种项目类型 FTP自动上传 大部分具有Windows开发经验的程序员会比较喜欢使用Visual Studio&#xff0c;而大部分Unix/Linux程序员则喜欢使用UltraEdit直接在主机上写代码。 为什么直接在主机上写代码呢&#xff0c;因…

AIGC - Qwen大模型:Qwen-7B模型推理部署

硬件环境 作为AIGC方面的小白来说&#xff0c;我抱着非常天真的想法&#xff0c;想让它在我的工作笔记本上用i5的CPU去跑&#xff0c;至于为什么这么想&#xff0c;当然是因为我没有GPU&#xff0c;身边也没有其他的带显卡电脑 恰好&#xff0c;在腾讯云看到了GN7的显示优惠活…

内存DMA及设备内存控制详解

序言 对于PCIe 设备&#xff08;PCIe Endpoint&#xff09;来说&#xff0c;其和CPU CORE、DRAM 的交互&#xff0c;主要涉及两种类型的内存访问&#xff1a; 设备内存访问&#xff1a;PCIe 设备的 Device Memory&#xff08;设备内存&#xff09;的访问&#xff0c;例如CPU …