【算法-数组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,期待与你共同进步!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mac-postman存储文件目录

今天postman弹窗要求登录账号才可访问之前的API文档数据。 但是这postman的账号又是前同事的账号&#xff0c;我没有他的账号和密码啊。 登录了我自己的postman账号后&#xff0c;所有的api文档都不见了....我服了。 首先去屏幕左上角---> 前往 --->个人 然后键盘按显…

计算机网络重点概念整理-第二章 物理层【期末复习|考研复习】

第二章 物理层 【期末复习|考研复习】 计算机网络系列文章传送门&#xff1a; 第一章 计算机网络概述 第二章 物理层 第三章 数据链路层 第四章 网络层 第五章 传输层 第六章 应用层 第七章 网络安全 计算机网络整理-简称&缩写 文章目录 第二章 物理层 【期末复习|考研复习…

16、window11+visual studio 2022+cuda+ffmpeg进行拉流和解码(RTX3050)

基本思想:需要一个window11 下的gpu的编码和解码代码,逐开发使用,先上个图 几乎0延迟的,使用笔记本的显卡 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.0\extras\demo_suite>deviceQuery.exe deviceQuery.exe Starting...CUDA Device Query (Runtime API…

Python---练习:有一物,不知其数,三三数之余二,五五数之余三,七七数之余二,问物几何?

案例&#xff1a; 有一物&#xff0c;不知其数&#xff0c;三三数之余二&#xff0c;五五数之余三&#xff0c;七七数之余二&#xff0c;问物几何&#xff1f; 人话&#xff1a; 有一个数字&#xff0c;不知道具体是多少&#xff0c;用3去除剩2&#xff0c;用5去除剩3&#…

ubuntu扩大运行内存, 防止编译卡死

首先查看交换分区大小 grep SwapTotal /proc/meminfo 1、关闭交换空间 sudo swapoff -a 2、扩充交换空间大小&#xff0c;count64就是64G 1G x 64 sudo dd if/dev/zero of/swapfile bs1G count64 3、设置权限 sudo chmod 600 /swapfile 4、指定交换空间对应的设备文件 …

Linux操作系统概述3——进程相关操作讲解(进程概念、xinetd守护进程、进程管理命令)

目录 进程的概念 程序与进程的关系 进程的分类 守护进程的分类 进程的PID 进程的状态 xinetd 守护进程服务 xinetd基本概念 xinetd工作原理 xinetd相关文件介绍 守护进程的管理命令 chkconfig 命令 service 命令 systemctl命令 查看进程状态相关命令 一般程序处…

lesson2(补充)关于const成员函数

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 前言&#xff1a; 将const 修饰的 “ 成员函数 ” 称之为 const 成员函数 &#xff0c; const 修饰类成员函数&#xff0c;实际修饰该成员函数 隐含的 this 指针 &#xff0c;表明在该成员函数中不能对类的任何成员进行修改…

【C语言】字符函数、字符串函数与内存函数

简单不先于复杂&#xff0c;而是在复杂之后。 目录 0. 前言 1. 函数介绍 1.1 strlen 1.1.1 介绍 1.1.2 strlen 函数模拟实现 1.1.2.1 计数器方法 1.1.2.2 递归方法 1.1.2.3 指针 - 指针方法 1.2 strcpy 1.2.1 介绍 1.2.2 strcpy 函数模拟实现 1.3 strcat 1…

听GPT 讲Rust源代码--library/std(8)

题图来自Why is Rust programming language so popular?[1] File: rust/library/std/src/sys/sgx/abi/reloc.rs 在Rust源代码中&#xff0c;sgx/abi/reloc.rs文件的作用是定义了针对Intel Software Guard Extensions (SGX)的重定位相关结构和函数。 该文件中的Rela 结构定义了…

计算机毕设 基于CNN实现谣言检测 - python 深度学习 机器学习

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 Hi&#xff0c;大家好&#xff0c;这里是丹成学长&#xff0c;今天向大家介绍 一个深度学习项目 基于CNN实现谣言检测 1.1 背景 社交媒体的发展在加速信息传播的…

Linux:firewalld防火墙-(实验2)-IP伪装与端口转发(4)

实验环境 本章实验环境要建立在上一章之上&#xff0c;ip等都是继承上一章&#xff0c;完全在上一章之下的操作 Linux&#xff1a;firewalld防火墙-小环境实验&#xff08;3&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/133996151?spm1001.2014.3…

消息队列中间件面试笔记总结RabbitMQ,Kafka,RocketMQ

文章目录 (一) Rabbit MQRabbitMQ 核心概念消息队列的作用Exchange(交换器)Broker&#xff08;消息中间件的服务节点&#xff09;如何保证消息的可靠性如何保证 RabbitMQ 消息的顺序性如何保证 RabbitMQ 高可用的&#xff1f;如何解决消息队列的延时以及过期失效问题消息堆积问…

【JAVA学习笔记】48 - 八大常用Wrapper类(包装类)

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter13/src/com/yinhai/wrapper_ 内的wrapper 一、包装类 1.针对八种基本定义相应的引用类型一包装类 2.有了类的特点&#xff0c;就可以调用类中的方法。 黄色背景的表示父类是Number 二、包装…

一、【海报合成的流程】

文章目录 主体创意草图素材拼图光影调色 主体 首先联想主体相关的关键词 创意 将联想到的关键词&#xff0c;串起来生成创意 草图 结合主体跟创意&#xff0c;我们先绘制一幅草图。草图可以是简单的图形&#xff0c;然后组成大概的结构布局。 素材 根据草图去寻找我们需…

CLIP文章精读

核心&#xff1a; loss的设计&#xff1a;分布针对固定image匹配text和固定text匹配image设计了两个交叉熵loss

水性杨花:揭秘CSS响应式界面设计,让内容灵活自如,犹如水之变幻

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、是…

Python--练习:报数字(数7)

案例&#xff1a; 一些同学从1开始报数&#xff0c;当需要报出的数字尾数是7或者该数字是7的倍数时&#xff0c;则该同学跳过这个数字&#xff0c;不进行报数。所有同学都参与游戏后&#xff0c;游戏结束。如输入学生数量为50&#xff0c;游戏结束后&#xff0c;报数的同学数量…

【机器学习】sklearn特征值选取与处理

sklearn特征值选取与处理 文章目录 sklearn特征值选取与处理1. 调用数据集与数据集的划分2. 字典特征选取3. 英文文本特征值选取4. 中文特征值选取5. 中文分词文本特征抽取6. TfidfVectorizer特征抽取7. 归一化处理8. 标准化处理9. 过滤低方差特征10. 主成分分析11. 案例&#…

基于Android 10系统的ROC-RK3399-PC Pro源码编译

基于Android 10系统的ROC-RK3399-PC Pro源码编译 一、开发环境搭建二、下载Android 10 SDK三、编译Android 10 SDK ROC-RK3399-PC Pro资料下载处&#xff1a;https://www.t-firefly.com/doc/download/145.html一、开发环境搭建 Android 10 SDK的编译对PC机的要求不低&#xff…

软考系列(系统架构师)- 2012年系统架构师软考案例分析考点

试题一 软件架构&#xff08;架构风格对比、架构风格选取、架构设计过程&#xff09; 【问题1】&#xff08;12分&#xff09; 请用200字以内的文字解释什么是软件架构风格&#xff0c;并从集成开发环境与用户的交互方式、集成开发环境的扩展性、集成开发环境的数据管理三个方…