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

news2024/11/18 7:36:40

今天,带来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.coloradmin.cn/o/1139785.html

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

相关文章

AMEYA360:炬玄智能车规级RTC芯片JXR191T为车载BMS提供16年稳态输出

北京炬玄智能科技有限公司聚焦于集成电路时钟芯片研发及生产&#xff0c;以高精度、高稳定性、集成化芯片和系统设计为主要方向&#xff0c;以实时时钟芯片(RTC)及模组为切入点&#xff0c;逐步将产品拓展到以TCXO、OCXO为代表的高端晶振芯片领域&#xff0c;最终打通整个时钟产…

探讨Java多线程调度:如何实现两线程并行,一线程等待?

亲爱的小伙伴们&#xff0c;大家好&#xff01;我是小米&#xff0c;很高兴再次和大家分享一些关于Java编程的有趣技巧和知识。今天&#xff0c;我们将探讨一个有趣且常见的面试问题&#xff1a;如何让两个线程同时执行&#xff0c;而第三个线程必须等待前两个线程结束后才能开…

trucksim常见问题

一、Error: Unable to load .vs data from “D:\Users\Public\Documents\TruckSim2019.0 Data\Results\Run_e24aa2… LastRun.vs”.Reason for failure: Invalid character OxFFFFFFB2 in string"" on line 4.Would you like to continue receiving alerts of this t…

RPA厂商大比拼,哪家才更适合您?

引言&#xff1a;随着数字化时代的到来&#xff0c;自动化已成为推动企业数字化发展的关键举措之一&#xff0c;RPA作为自动化中的重要技术之一&#xff0c;可为企业提供了实现业务流程自动化的强大工具。然而&#xff0c;如何选择适合自己的RPA厂商也是各大企业现在面临的难题…

调试-Debug

0.1 Debug环境介绍 Microsoft Visual Studio 2022中&#xff1a; Debug版本的可执行程序称为调试版本&#xff0c;包含调试信息&#xff0c;不作任何优化&#xff0c;便于程序员进行调试。 Release版本的可执行程序称为发布版本&#xff0c;进行了各种优化&#xff0c;不可调…

小红书内容运营包含哪些,内容种草攻略

在这个社交属性&#xff0c;强势泛滥的年代&#xff0c;兼具了社交和电商两大功能的小红书&#xff0c;已经成为品牌方的兵家必争之地。今天来为大家分享下小红书内容运营包含哪些&#xff0c;内容种草攻略&#xff01; 1、确定账号定位 这是做好小红书内容运营的第一步。一个有…

扫地机器人,不相信视觉导航

不可置否&#xff0c;激光雷达已经成为扫地机器人的“耶路撒冷”。 导航技术的从无到有 回顾扫地机器人的兴衰&#xff0c;本质是导航技术的从无到有、从弱到强、从少到多&#xff0c;而在这个过程中&#xff0c;激光雷达无疑发挥了无可替代的作用。2010年&#xff0c;第一台…

实时嵌入式系统环境中敏捷的基础

不同的人使用敏捷一词的含义不同。在计算中&#xff0c;该术语最初用于描述执行项目开发的轻量级方法&#xff0c;而最初的术语极限编程&#xff08;XP&#xff09;未能激发受托管理开发项目的众多管理人员。 基本上&#xff0c;敏捷软件开发指的是一组松散集成的原则和实践&a…

Vue echarts 折线图 背景颜色渐变 (两种实现方式)

需求 实现方式 两种方法 方法一&#xff1a;color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{}&#xff0c;{}&#xff0c;{}]) 方法二&#xff1a;避开new echarts&#xff0c;color: {x: 0, y: 0, x2: 0, y2: 1,colorStops: [{}&#xff0c;{}&#xff0c;{}]} …

【嵌入式】HC32F07X DAC模拟输出 + DMA传输

一 背景说明 使用小华&#xff08;华大&#xff09;的MCU HC32F07X实现两个通道的 0-5V 电压模拟输出。 二 原理分析 【1】DAC原理说明&#xff1a; 所谓DAC&#xff0c;就是Digital-Analog-Converter&#xff0c;数字模拟转换器。在模拟电路中&#xff0c;电流电压变化是连续…

漏洞复现-dedecms文件上传(CVE-2019-8933)

dedecms文件上传_CVE-2019-8933 漏洞信息 Desdev DedeCMS 5.7SP2版本中存在安全漏洞CVE-2019-8933文件上传漏洞 描述 ​ Desdev DedeCMS&#xff08;织梦内容管理系统&#xff09;是中国卓卓网络&#xff08;Desdev&#xff09;公司的一套基于PHP的开源内容管理系统&#x…

WebSocket 原理揭秘:让你彻底搞懂 Websocket 原理

WebSocket 的原理 WebSocket 是什么&#xff1f; WebSocket 是一种新型的协议&#xff0c;它可以在客户端和服务器之间建立长连接&#xff0c;实现双向通信。在传统的 HTTP 协议中&#xff0c;当客户端向服务器发送请求后&#xff0c;服务器会返回响应&#xff0c;然后连接就…

创建python虚拟环境

为什么要创建python虚拟环境&#xff1f; 在python开发中&#xff0c;我们可能会遇到一种情况&#xff0c;就是当前的项目依赖的是某一个版本&#xff0c;但是另一个项目依赖的是另一个版本&#xff0c;这样就会造成依赖冲突&#xff0c;在不同的环境中进行不同需求的项目开发…

ChinaSoft 论坛巡礼 | 智慧化 IDE 论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

华为OD机考算法题:矩阵最大值

题目部分 题目矩阵最大值难度难题目说明给定一个仅包含 0 和 1 的 N*N 二维矩阵&#xff0c;请计算二维矩阵的最大值&#xff0c;计算规则如下&#xff1a; 1. 每行元素按下标顺序组成一个二进制数&#xff08;下标越大越排在低位&#xff09;&#xff0c;二进制数的值就是该行…

怎样下载视频号视频?分享6种有效方法

在今天的数字时代&#xff0c;视频号视频成为了人们生活中不可或缺的一部分。有时候&#xff0c;我们看到了一段精彩的视频&#xff0c;希望能够保存下来欣赏或分享给朋友&#xff0c;却因为平台限制而困难重重。为了帮助你实现这个目标&#xff0c;本文将介绍几种简单而有效的…

【JAVA基础】多线程与线程池

多线程与线程池 文章目录 多线程与线程池1. 相关概念1.1 线程调度1.2 守护线程 2. 生命周期3. 同步机制/同步锁3.1 synchronized3.2 lock3.3 synchronized 与 Lock 的对比 4. 死锁5. 线程通信5.1 线程间的通信5.2 等待唤醒机制5.3 举例5.4 调用 wait 和 notify 需注意的细节5.5…

docker应用部署---MySQL的部署配置

docker应用部署---MySQL的部署配置 1. 搜索mysql镜像2. 拉取mysql镜像3. 创建容器&#xff0c;设置端口映射、目录映射4. 进入容器&#xff0c;操作mysql5. 登录mysql6. 使用外部机器连接容器中的mysql 1. 搜索mysql镜像 docker search mysql2. 拉取mysql镜像 #安装5.6版本的M…

<script> 标签的 defer 和 asnyc 属性的作用以及二者的区别

前言 在现代Web开发中&#xff0c;JavaScript的使用几乎是不可避免的。随着Web应用变得越来越复杂&#xff0c;JavaScript文件的大小也在不断增长。为了提高页面加载性能&#xff0c;浏览器提供了async和defer两个属性&#xff0c;用于改变浏览器加载和执行JavaScript文件的方…

AI数字人系统源码部署,打造自己品牌的数字人克隆直播平台

AI数字人是一种利用人工智能技术生成的虚拟形象&#xff0c;可以模仿真人的外貌、声音、表情和动作&#xff0c;实现与人类的自然交互。AI数字人有着广泛的应用场景&#xff0c;如虚拟主播、电商直播、客服助理、教育培训等。要实现AI数字人的功能&#xff0c;需要部署一套完整…