【刷题笔记】之二分查找(搜索插入位置。在排序数组中查找元素的第一个和最后一个位置、x的平方根、有效的完全平方数)

news2024/11/17 21:51:11

1. 二分查找

题目链接 704. 二分查找 - 力扣(LeetCode)

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

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

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

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。

  1. n 将在 [1, 10000]之间。

  1. nums 的每个元素都将在 [-9999, 9999]之间。

思路

进行二分查找的前置条件:数组有序并且数组中无重复元素

写二分查找题的代码,需要考虑清楚边界条件,比如应该是 while(left < right) 还是 where(left <= right),应该是 right = mid - 1 还是 right = mid

主要在写代码时要考虑清楚区间的范围,必须要在二分查找的过程中,根据范围保持不变量,也就是在 while 中每一次边界的处理根据区间进行操作

写二分查找,区间的定义一般有两种,左闭右闭 [left , right],左闭右开 [left , right)

写法一:左闭右闭

首先明确,我们定义的 target 是在区间 [left , right] 中,其次根据范围确定两点

  • while (left <= right) 这里要使用 <=,因为是左闭右闭的区间, left == right 有意义

  • if (nums[mid] > target) right = mid - 1,因为 nums[mid] > target,所以当前这个 nums[mid] 一定不是 target,并且是左闭右闭区间,所以缩小范围,right = mid - 1

class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        if (target < nums[left] || target > nums[right]) {
            return -1;
        }
        while (left <= right) {
            int mid = left + (right - left)/2;
            if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
}

写法二:左闭右开

首先明确,我们定义的 target 是在区间 [left , right) 中,其次根据范围确定两点

  • while (left < right) 这里要使用 <, left == right 没有意义,因为是左闭右开的区间

  • if (nums[mid] > target) right = mid ,因为 nums[mid] > target,所以当前这个 nums[mid] 一定不是 target,那么就要去左区间寻找,因为区间范围是左闭右开的,所以 right = mid,下一次查询区间不会去比较 nums[mid]

class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length;
        while (left < right) {
            int mid = left + (right - left)/2;
            if (nums[mid] > target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
}

2. 搜索插入位置

题目链接:35. 搜索插入位置 - 力扣(LeetCode)

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

提示:

  1. 1 <= nums.length <= 104

  1. -104 <= nums[i] <= 104

  1. nums 为 无重复元素 的 升序 排列数组

  1. -104 <= target <= 104

思路

在数组中插入目标值 target,只有这四种情况

  • target 在数组所有元素之前

  • target == 数组中某一个元素

  • target 插入数组中位置

  • target 在数组所有元素之后

写法一:暴力解法

这道题最为直接的暴力解法,遍历数组中每个元素,只要发现遍历到某个元素 >= target 时直接返回下标,如果遍历完 target 是最大的那就返回 nums.length 直接插入到最后

class Solution {
    public int searchInsert(int[] nums, int target) {
        for(int i = 0; i < nums.length; i++) {
            if(target <= nums[i]) {
                return i;
            }
        }
        return nums.length;
    }
}
  • 时间复杂度 O(n)

  • 空间复杂度 O(1)

写法二:二分法

使用二分法前提,数组有序,并且无重复元素

确定边界 [left , right]

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while(left <= right) {
            int mid = left + (right - left)/2;
            if(nums[mid] == target) {
                return mid;
            } else if(nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        // 分析四种上面四种插入的情况
        // target 在数组所有元素之前 0
        // target == 数组某个元素  mid
        // target 插入到数组中某个位置 right + 1 
        // target 在数组所有元素之后 right + 1
        return right + 1;
    }
}
  • 时间复杂度 O(log n)

  • 空间复杂度 O(1)

3. 在排序数组中查找元素的第一个和最后一个位置

题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  1. 0 <= nums.length <= 105

  1. -109 <= nums[i] <= 109

  1. nums 是一个非递减数组

  1. -109 <= target <= 109

思路:

寻找 target 在数组中的左右边界,有这三种情况:

  1. target 在数组范围的右边或左边,比如数组 {3,4,5},target = 2 或 target = 6,此时应该返回 {-1,-1}

  1. target 在数组范围中,且数组中不存在 target,比如数组 {3,4,7},target = 6,此时应该返回 {-1,-1}

  1. target 在数组范围中,且数组中存在 target,比如数组 {3,4,5},target = 4,此时应该返回 {1, 1}

下面就是找左右边界了,采用二分法寻找左右边界

写法一:二分法

二分查找,寻找 target 的右边界(不包括 target)

如果 rightBorder 为没有被赋值(即 target 在数组范围的左边,比如 数组 {3,3},target = 2,为了处理这种情况)

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);

        // 情况一
        if (leftBorder == -2 || rightBorder == -2) {
            return new int[]{-1, -1};
        } 
        // 情况三
        if (rightBorder - leftBorder > 1) {
            return new int[]{leftBorder+1, rightBorder-1};
        }
        // 情况二
        return new int[]{-1, -1};
    }

    private int getLeftBorder(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        int leftBorder = -2;
        while (left <= right) {
            int mid = left + (right - left)/2;
            if (target <= nums[mid]) {
                // 找左边界,nums[mid]==target 时更新 right
                right = mid - 1;
                leftBorder = right;
            } else {
                left = mid + 1;
            }
        }
        return leftBorder;
    }

    private int getRightBorder(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        int rightBorder = -2;
        while (left <= right) {
            int mid = left + (right - left)/2;
            if (target >= nums[mid]) {
                //找右边界,nums[mid]==target 时更新 left
                left = mid + 1;
                rightBorder = left;
            } else {
                right = mid - 1;
            }
        }
        return rightBorder;
    }
}

写法二:二分法+双指针(推荐)

  1. 在 nums 数组中二分查找 target

  1. 如果二分查找失败,则 binarySearch 返回 -1,表名 nums 中没有 target,此时直接返回 {-1,-1}

  1. 如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标,通过左右滑动指针,来找到符合题意的区间

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int index = binarySearch(nums, target);
        if(index == -1) {
            return new int[]{-1,-1};
        }    
        int left = index, right = index;
        while(left - 1 >= 0 && nums[left - 1] == target) {
            left--;
        }
        while(right + 1 <= nums.length - 1 && nums[right + 1] == target) {
            right++;
        }
        return new int[]{left,right};
    }

    private int binarySearch(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while(left <= right) {
            int mid = left + (right - left)/2;
            if(target == nums[mid]) {
                return mid;
            } else if(target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }
}

4. x 的平方根

题目链接:69. x 的平方根 - 力扣(LeetCode)

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例 1:

输入:x = 4
输出:2

示例 2:

输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

提示:

0 <= x <= 231 - 1

class Solution {
    public int mySqrt(int x) {
        long left = 0,right = x,ans = -1;
        while(left <= right) {
            long mid = left + (right - left)/2;
            if((long)(mid*mid) <= x) {
                ans = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return (int)ans;
    }
}

5. 有效的完全平方数

题目链接:367. 有效的完全平方数 - 力扣(LeetCode)

给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。

不能使用任何内置的库函数,如 sqrt 。

示例 1:

输入:num = 16
输出:true
解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。

示例 2:

输入:num = 14
输出:false
解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。

提示:

1 <= num <= 231 - 1

class Solution {
    public boolean isPerfectSquare(int num) {
        int left = 0, right = num;
        while(left <= right) {
            int mid = left + (right - left)/2;
            long square = (long)mid*mid;
            if(square > num) {
                right = mid - 1;
            } else if(square < num) {
               left = mid + 1;
            } else {
                return true;
            }
        }
        return false;
    }
}

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

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

相关文章

《Linux运维实战:Mysql8.0.30安装及卸载component_validate_password插件》

一、背景 由于业务系统的特殊性&#xff0c;我们需要将MySQL8.0.30主从复制集群部署在客户机房服务器上&#xff0c;且客户对数据库的密码策略要求比较高。 因为官方的8.0.30版本的Docker镜像默认是没有安装validate_password插件的&#xff0c;所以我在主从复制集群安装完成后…

minGW-w64配置途径

文章目录1 GNU、GCC与minGW2 minGW当前下载方式3 minGW-w64配置途径Step1Step2Step31 GNU、GCC与minGW GNU这个名字是GNUs Not Unix的递归首字母缩写&#xff0c;它的发音为[gnoo]&#xff0c;只有一个音节&#xff0c;发音很像"grew"&#xff0c;但需要把其中的r音替…

aws apigateway 基础概念和入门示例

参考资料 https://docs.aws.amazon.com/zh_cn/apigateway/latest/developerguide/getting-started.html apigateway基础理解 apigateway的核心概念 apigateway&#xff0c;基础服务用来管理接口的创建&#xff0c;部署和管理restapi&#xff0c;http资源和方法的集合&#…

【LeetCode】1599. 经营摩天轮的最大利润

1599. 经营摩天轮的最大利润 题目描述 你正在经营一座摩天轮&#xff0c;该摩天轮共有 4 个座舱 &#xff0c;每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱&#xff0c;但每次轮转都需要支付一定的运行成本 runningCost 。摩天轮每次轮转都恰好转动 1 / 4 周。 …

7 Seata简介

Seata-Server安装 分布式事务解决方案 2PC即两阶段提交协议&#xff0c;是将整个事务流程分为两个阶段&#xff0c;P是指准备阶段&#xff0c;C是指提交阶段。 1. 准备阶段&#xff08;Prepare phase&#xff09; 2. 提交阶段&#xff08;commit phase&#xff09;举例&…

模电基础(2)半导体二极管

1.二极管的组成二极管&#xff1a;将PN结封装起来&#xff0c;引出两个电极就构成了半导体二极管。二极管的常见结构包括&#xff1a;点接触型&#xff08;图a&#xff09;&#xff0c;面接触型&#xff08;图b&#xff09;&#xff0c;平面型&#xff08;图c&#xff09;。 点…

世界顶级五大女程序媛,不仅技术强还都是美女

文章目录1.计算机程序创始人&#xff1a;勒芙蕾丝伯爵夫人2.首位获得图灵奖的女性&#xff1a;法兰艾伦3.谷歌经典首页守护神&#xff1a;玛丽莎梅耶尔4.COBOL之母&#xff1a;葛丽丝穆雷霍普5.史上最强游戏程序媛-余国荔说起程序员的话&#xff0c;人们想到的都会是哪些理工科…

java基础-标识符命名规范和数据类型

标识符 1.什么是标识符&#xff1f; Java中变量、方法、类等要素命名时使用的字符序列&#xff0c;称为标识符。 技巧&#xff1a;凡是自己可以起名字的地方都叫标识符。比如&#xff1a;类名、方法名、变量名、包名、常量名等 2.标识符的命名规则 1.标识符由26个英文字母大小…

LQB手打,18B20读取温度,放大一百倍

https://blog.csdn.net/qq_45225613/article/details/110303632?ops_request_misc%257B%2522request%255Fid%2522%253A%2522167798888716800215065334%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id167798888716800215065334&biz_id0&a…

RCNN

1.RCNN 1.1算法流程 一张图像生成1k~2k个候选区域(使用Selective Search方法)对每个候选区域&#xff0c;使用深度网络提取特征特征送入每一类的SVM分类器&#xff0c;判断是否属于该类使用回归器精细修正候选框位置 1.候选区域的生成 利用selective Search算法通过图像分割的方…

【erlang】入门篇

欢迎入坑Erlang&#xff0c;关键字&#xff1a; 函数式&#xff0c;高并发&#xff0c;容错&#xff0c;热更新&#xff0c;分布式 安装 erlang的安装非常简单&#xff0c;直接去官网下载编译好的二进制安装包即可。需要注意的是文件名格式是OTP_平台_版本.后缀&#xff0c;其…

2021天梯赛真题题解 L1-3 强迫症 (10 分) C语言版本 整数转换为字符串数组

L1-3 强迫症 (10 分) 题目描述 小强在统计一个小区里居民的出生年月&#xff0c;但是发现大家填写的生日格式不统一&#xff0c;例如有的人写 199808&#xff0c;有的人只写 9808。有强迫症的小强请你写个程序&#xff0c;把所有人的出生年月都整理成 年年年年-月月 格式。对于…

一文说透容器跨主机网络

文章目录一、Flannel1、UDP2、VXLAN&#xff08;1&#xff09;VXLAN核心流程总结&#xff08;2&#xff09;VTEP隧道通信流程详解【1】封装 inner Ethernet header&#xff08;依据VTEP IP查MAC&#xff09;【2】设置VNI&#xff08;标识数据包应该交给那个处理设备&#xff09…

AI绘画第二步,抄作业复现超赞的效果!

上一篇&#xff0c;讲了如何安装AI绘画软件&#xff0c;但是装完后发现生成效果很渣&#xff01;而网上那些效果都很赞。真的是理想很丰满&#xff0c;现实很骨感。今天就是来聊聊如何抄作业&#xff0c;最大程度的还原那些超赞的效果。换一种说法就是&#xff0c;教大家如何使…

spark sql(一)源码分析sql解析流程

spark sql解析sql主要基于Catalyst框架&#xff0c;它将复杂的sql解析分为很多的阶段&#xff0c;每个阶段基本都有专属的工具类和扩展接口&#xff0c;最终实现将sql转换为DataFrame或RDD任务的功能。如果对于这些中间阶段和工具类没有一个整体概念性的了解&#xff0c;那阅读…

JUC并发编程与源码分析笔记11-Java对象内存布局和对象头

先从阿里及其它大厂面试题说起 你觉得目前面试&#xff0c;你还有那些方面理解的比较好&#xff0c;我没问到的&#xff0c;我说了juc和jvm以及同步锁机制那先说juc吧&#xff0c;说下aqs的大致流程cas自旋锁&#xff0c;是获取不到锁就一直自旋吗?cas和synchronized区别在哪…

国内的PMP考试通过率高达97%?

自认为是虚高&#xff0c;虽然国人在考试方面的确独树一帜的强&#xff0c;应该也没有这样夸张。 如果自学&#xff0c;大概是50%&#xff0c;如果有老师教&#xff0c;那大概是60%到80%&#xff0c;还是比较高的。 为什么自学那么低&#xff1f;除了自身的自制力的问题&…

【编程基础之Python】9、Python中的变量

【编程基础之Python】9、Python中的变量Python中的变量变量的定义和赋值变量的命名规范变量的类型变量的作用域变量的赋值特殊的变量删除变量总结Python中的变量 在Python中&#xff0c;变量是用来存储数据的一种方式。Python是一种动态类型语言&#xff0c;因此在声明变量时不…

JWT利用在ctfhub-easy_login拿到flag

目录 什么是JWT&#xff1f; jwt由三个部分组成&#xff1a;header.payload.signature header部分&#xff1a; payload部分&#xff1a;声明 signature部分&#xff1a; JWT验证过程&#xff1a; ctfhub-easy_login 目的&#xff1a;拿到flag 过程分析以及实操&#x…

阿里云轻量服务器--Docker--Nacos安装(使用外部Mysql数据存储)

前言&#xff1a;docker 安装nacos 如果不设置外部的mysql 默认使用内嵌的内嵌derby为数据源&#xff0c;这个时候如果&#xff0c;重新部署nacos 则会造成原有数据丢失情况&#xff1b; 1 默认安装的nacos 启动后使用的是内嵌的存储&#xff1a; 2 使用外部mysql 作为存储&a…