LeetCode_Day1 | 关于数组双指针及二分法查询

news2024/11/27 14:43:59

LeetCode_数组

  • 704.二分查找
    • 1.题目描述
    • 2. 做题前要想明白什么
    • 3. 左闭右闭代码
    • 4.左闭右开代码
    • 5. 关于中间值溢出问题
    • 6. 图解举例(左闭右闭)
  • 27.移除元素
    • 1. 题目描述
    • 2. 暴力法
    • 3. 快慢指针法
    • 4. 双向双指针法

704.二分查找

1.题目描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

详情LeetCode题链接:https://leetcode.cn/problems/binary-search/

2. 做题前要想明白什么

注:本题前提已满足数组为有序数组,同时题目还强调数组中无重复元素,满足以上两点即可以使用二分查找法。

因为数组内一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的必要前提。

  1. 二分法的整体思想实际就是不断的循环区间,如何确定边界是重点。 比如到底是while(left < right) 还是 while (left <= right) ,是right = mid ,还是right = mid - 1?

  2. 首先要理解区间的定义,区间定义就是不变量。
    在二分查找的过程中,要保持不变量,即在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。

    比如采用左闭右闭区间时,通过对比中间值和target值要确定下一次查找区间,那么下一次的查找区间也是左闭有闭区间。

  3. 区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。

  4. 个人理解:左闭右开和左闭右闭相比可以理解为,左闭右闭比左闭右开多一个假想数,如下图:
    在这里插入图片描述

3. 左闭右闭代码

 /**
     * 思路:二分法的边界值是重点,灵魂是如何考虑区间,常用区间:
     * 左闭右闭:[left,right],即当left=right是也成立,比如[1,1]所以边界可设为while(left<=right);当nums[mid]>target时,因为此时是右闭区间,此
     *          区间是包含右边的值的,所以当nums[mid]已经大于target了,所以nums[mid]一定不是target,right=mid-1;
     * 左闭右开:[left,right),即当left=right不成立,比如[1,1)所以边界可设为while(left<right);当nums[mid]>target时,因为此时是右开区间,此
     *          区间是不包含右边的值的,所以当nums[mid]已经大于target了,所以nums[mid]一定不是target,right=mid;
     *
     * 本次实现采用左闭右闭区间写法
     * 时间复杂度O(logn),空间复杂度O(1)
     */
    public int search(int[] nums, int target) {
        //避免当target不存在于有序数组nums中造成多次循环运算
        if (target < nums[0] || target > nums[nums.length -1]) {
             return -1;
        }
        int left = 0, right = nums.length - 1;
        while (left<=right){
            int mid = left + ((right - left) >> 1);
            if (nums[mid] == target) 
                return mid;
            else if (nums[mid] > target)
                right = mid - 1;
            else if (nums[mid] < target)
                left = mid + 1; 
        }
        return -1;
    }

4.左闭右开代码

 /**
     * 思路:二分法的边界值是重点,灵魂是如何考虑区间,常用区间:
     * 左闭右闭:[left,right],即当left=right是也成立,比如[1,1]所以边界可设为while(left<=right);当nums[mid]>target时,因为此时是右闭区间,此
     *          区间是包含右边的值的,所以当nums[mid]已经大于target了,所以nums[mid]一定不是target,right=mid-1;
     * 左闭右开:[left,right),即当left=right不成立,比如[1,1)所以边界可设为while(left<right);当nums[mid]>target时,因为此时是右开区间,此
     *          区间是不包含右边的值的,所以当nums[mid]已经大于target了,所以nums[mid]一定不是target,right=mid;
     *
     * 本次实现采用左闭右开区间写法
     * 时间复杂度O(logn),空间复杂度O(1)
     */
    public int search(int[] nums, int target) {
        //避免当target不存在于有序数组nums中造成多次循环运算
        if (target < nums[0] || target > nums[nums.length -1]) {
             return -1;
        }
        int left = 0, right = nums.length;
        while (left < right){
            int mid = left + ((right - left) >> 1);
            if (nums[mid] == target) 
                return mid;
            else if (nums[mid] > target)
                right = mid;
            else if (nums[mid] < target)
                left = mid + 1; 
        }
        return -1;
    }

5. 关于中间值溢出问题

  1. 通常中间值一般写法: mid = (right+left)/2;

  2. 考到溢出和效率后优化使用: mid = (right-left) >>1+left

  3. 解释:
    (right-left)/2+left(right-left)>>1+left 都是用来计算两个数 left 和 right 之间的中点。

    它们都等价于 (right+left)/2。但是 (right-left)>>1+left 使用位移运算代替了除以 2,效率高一点。而且前面两种写法可以避免 int 溢出。

6. 图解举例(左闭右闭)

例如输入: nums = [-1,0,3,5,9,12], target = 9
在这里插入图片描述

27.移除元素

1. 题目描述

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

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

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

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

详情LeetCode题链接: https://leetcode.cn/problems/remove-element/

2. 暴力法

数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖.

采用暴力解法,两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组

代码逻辑:

/**
     * 思路:
     *     数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖.
     *     采用暴力解法,两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组 
     * 时间复杂度:O(n^2)
     * 空间复杂度:O(1)   
     */
    public int removeElement(int[] nums, int val) {
        int len = nums.length;
        for (int i = 0; i < len; i++){
            if (nums[i] == val){
                for (int j = i + 1; j< len; j++){
                    nums[i] = nums[j];
                }
                i --;
                len --;
            }
        }
        return len;
    }

3. 快慢指针法

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

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

代码逻辑:

 /**
     * 思路:快慢指针法
     *     数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖.
     *     通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
     *     快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
     *     慢指针:指向更新 新数组下标的位置
     * 时间复杂度:O(n)
     * 空间复杂度:O(1)   
     */
    public int removeElement(int[] nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex ++){
            if (nums[fastIndex] != val){
                nums[slowIndex] = nums[fastIndex];
                slowIndex ++;
            }
        }
        return slowIndex;
    }

4. 双向双指针法

因为不考虑新数组内的顺序,可用相向双指针法

代码实现逻辑:

/**
     * 思路:相向双指针法(因为不考虑新数组内的顺序,可用相向双指针法)
     *     数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖.
     *     通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
     *     右指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
     *     左指针:指向更新 新数组下标的位置
     * 时间复杂度:O(n)
     * 空间复杂度:O(1)   
     */
    public int removeElement(int[] nums, int val) {
        int left = 0;
        int right = nums.length -1;
        //因为不考虑新数组内的顺序,将right移到从右数第一个值不为val的位置
        while (right >= 0 && nums[right] == val) {
            right --;
        }
        //因为不考虑新数组内的顺序,将right位置的元素移到left(覆盖),right位置移除
        while (left <= right){
            if (nums[left] == val){
                nums[left] = nums[right];
                right --;
            }
            left ++;
            while (right >= 0 && nums[right] == val){
                right --;
            }
        }
        return left;
    }

部分图解理解如下:
在这里插入图片描述

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

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

相关文章

15天学习MySQL计划(运维篇)分库分表-监控-第十四天

15天学习MySQL计划分库分表-监控-第十四天 1.介绍 1.问题分析 ​ 随着互联网及移动互联网的发展&#xff0c;应用系统的数据量也是成指数式增加&#xff0c;若采用但数据进行数据存储&#xff0c;存在以下性能瓶颈&#xff1a; IO瓶颈&#xff1a;热点数据太多&#xff0c;数…

【2023/05/14】Apple I

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第9天。 Share I cannot choose the best.The best choose me. 译文&#xff1a; 我不能选择那最好的。 是那最好的选择我。 They throw their shadows before them who carry their lantern on thei…

MySQL基础-多表查询

本文介绍MySQL的多表查询 文章目录 多表关系一对多多对多一对一 查询内连接外连接自连接联合查询子查询标量子查询列子查询行子查询表子查询 多表查询案例 多表关系 一对多&#xff08;多对一&#xff09;多对多一对一 一对多 案例&#xff1a;部门与员工 关系&#xff1a;一…

Go Wails Docker图形界面管理工具 (4)

文章目录 1. 前言2. 效果图3. 代码 1. 前言 接上篇&#xff0c;本次添加Docker日志查看功能 2. 效果图 3. 代码 直接调用官方库 app.go func (a *App) Log(ID string) (string, error) {reader, err : Cli.ContainerLogs(context.Background(), ID, types.ContainerLogsOption…

最快鉴别网工能力的方法,就三个字

大家好&#xff0c;我是老杨。 可以投个票&#xff0c;让我了解了解你的近况。 转眼2023年都过去3天了&#xff0c;我这的人事部门又开始找我问招聘画像&#xff0c;每年这时候&#xff0c;我都得头痛一阵子。 从技术转管理之后&#xff0c;最明显的差异&#xff0c;就是从“…

TLS反调试

一、TLS概念 线程局部存储&#xff08;Thread Local Storage&#xff0c;TLS&#xff09;是一种线程级别的存储机制&#xff0c;它允许每个线程在运行时都拥有自己的私有变量&#xff0c;这些变量只能被该线程访问&#xff0c;而不会被其他线程所共享。 1、TLS的出现是为了解…

【 Python 全栈开发 ⑭ 】数据操作方法

文章目录 一、运算符&#xff08;1&#xff09;&#xff1a;加号二、运算符&#xff08;2&#xff09;&#xff1a;乘号三、in 与 not in四、len()五、del六、max() 与 min()七、数据类型转换 一、运算符&#xff08;1&#xff09;&#xff1a;加号 运算符 “” 的作用是 “合…

〖Web全栈开发②〗—网络编程基础(下)

〖Web全栈开发②〗—网络编程基础&#xff08;下&#xff09; &#xff08;一&#xff09;TCP 网络应用程序开发流程1. TCP 网络应用程序开发流程的介绍2. TCP 客户端程序开发流程的介绍3. TCP 服务端程序开发流程的介绍4. 小结 &#xff08;二&#xff09;socket之send和recv原…

4.LiCTF NSSCTF WEB方向部分 WriteUp

文章目录 0x01、我FLAG呢&#xff1f;【源码信息泄露js信息泄露】0x02、导弹迷踪【js信息泄露】0x03、Follow me and hack me【hackbarburp备份文件】0x04、PHP是世界上最好的语言&#xff01;&#xff01;【代码执行 getshell】0x05、Vim yyds【命令执行 getshell】0x06、作业…

孙鑫VC++第一章 Windows内部运行原理

1. Windows应用程序&#xff0c;操作系统&#xff0c;计算机硬件之间的相互关系 1箭头表示操作系统控制输出设备2箭头表示操作系统可以得到输入设备信息3箭头表示应用程序通知操作系统执行具体操作 操作系统提供给应用程序的接口 API4箭头表示输入设备变化告诉应用程序 Window…

线性表之双向链表(详解)

&#x1f355;博客主页&#xff1a;️自信不孤单 &#x1f36c;文章专栏&#xff1a;数据结构与算法 &#x1f35a;代码仓库&#xff1a;破浪晓梦 &#x1f36d;欢迎关注&#xff1a;欢迎大家点赞收藏关注 文章目录 &#x1f365;前言&#x1f352;双向链表1. 带头双向循环链表…

【C++】通序录管理系统

1、缘起 最近&#xff08;2023-04-24&#xff09;学习完了 C 编程语言的 基础语法&#xff0c;然后将这些基础语法的知识点整合到一起&#xff0c;实现一个 通讯录管理系统。以此来巩固以前所学习过的知识点&#xff0c;以求在后续的学习中能够灵活应用。 2、系统需求 通讯录是…

ChatGPT结合本地数据_llamaindex

1 功能 大模型学习的主要是通用数据&#xff0c;而用户可能需要让ChatGPT在本地的知识库中寻找答案。 普通用户不太可能训练大模型&#xff1b;由于本地数据格式丰富&#xff0c;内容烦多&#xff0c;且考虑到使用成本和token大小限制&#xff0c;也不可能在每次提问时都将所有…

balenaEtcher v1.18.1 开源跨平台镜像文件快速刻录工具

balenaEtcher 是一款开源免费的跨平台镜像文件快速刻录工具&#xff0c;使用体验感觉比软碟通UltraISO好用多了&#xff0c;推荐使用。它可以帮助用户快速将 ISO 文件、IMG 文件或者其他格式的镜像文件刻录到 USB 驱动器、SD 卡或者其他可烧录介质上。它支持 Windows、macOS 和…

50 Projects 50 Days - Blurry Loading 学习记录

项目地址 Blurry Loading 展示效果 Blurry Loading 实现思路 元素组成只需要有一张图片和中间的文本即可。针对动态过程分析初始和终止状态即可&#xff0c;初始时图片全模糊&#xff0c;文本显示0%&#xff1b;终止时&#xff0c;图片完全不模糊&#xff0c;文本会显示100…

Junit 单元测试框架(简单使用)

目录 一、注解 1. Test 2. BeforeEach 和 BeforeAll 3. AfterEach 和 AfterAll 二、断言 1. Assertions类 1.1 assertEquals 和 assertNotEquals 1.2 assertTrue 和 assertFalse 1.3 assertNull 和 assertNotNull 三、用例执行顺序 1. 方法的排序 —— Order 四、…

人工智能轨道交通行业周刊-第44期(2023.5.8-5.14)

本期关键词&#xff1a;智能列控、苏州城轨智慧大脑、智慧乘务系统、深铁智慧运维、铁路遥感、3D视觉 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通R…

【C++入门攻略】和【编程常见问题】

常见问题 vsstudio快捷键 快速注释组合键 ctrlk ctrlc 取消注释快捷键 ctrlk ctrl u 支持垃圾回收机制 大多数面向对象编程语言具有垃圾回收机制。早期的C语言不具备垃圾回收机制&#xff0c;这意味着申请的内存资源在使用完成后&#xff0c;需要程序员自己释放。直到C11标…

1066 Root of AVL Tree(51行代码+超详细注释)

分数 25 全屏浏览题目 切换布局 作者 CHEN, Yue 单位 浙江大学 An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebala…

孙鑫VC++第一章 Windows程序内部运行机制

目录 1.1 API和SDK 1.2 窗口和句柄 1.3 消息和队列 1.4 WinMain 1.4.1 WinMain函数的定义 1.4.2 窗口的创建 1.4.3 消息循环 1.4.4 窗口过程函数 1.1 API和SDK API:Windows操作系统提供给应用程序编程的接口。 SDK&#xff08;软件开发包&#xff09;:用于开发的所有资…