【LeetCode】剑指 Offer 53. 在排序数组中查找数字 p263 -- Java Version

news2025/4/28 2:12:20

1. 题目介绍(53. 在排序数组中查找数字)

面试题53在排序数组中查找数字 一共分为三小题:

  • 题目一:数字在排序数组中出现的次数
  • 题目二:0 ~ n-1 中缺失的数字
  • 题目三:数组中数值和下标相等的元素

2. 题目1:数字在排序数组中出现的次数

题目链接:https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/

2.1 题目介绍

统计一个数字在排序数组中出现的次数。

【测试用例】:
示例 1:

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

示例2:

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

【条件约束】:

提示

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

【相关题目】:

注意:本题与主站 34. 在排序数组中查找元素的第一个和最后一个位置 题目相同(仅返回值不同)。

2.2 题解 – 二分查找 – O(logn)

时间复杂度O(logn),空间复杂度O(1)

解题思路】:

  1. 暴力枚举:我们可以遍历整个数组,找到目标值就累加,统计出目标值的总数;
  2. 枚举简化:我们不遍历整个数组,而是从头开始找第一个目标值,从尾找最后一个目标值,但当目标值数量为1,且是中间元素时,时间复杂度和暴力枚举没有任何区别;
  3. 二分查找:我们将找第一个目标值和最后一共目标值的过程改为二分,通过二分查找可将时间复杂度降为 O(logn)

……
实现策略】:
二分查找算法总是先拿数组中间的数字和 k 作比较,以查找第一个目标值 k 为例:

  • 如果中间的数字比 k,那么 k 只有可能出现在数组的前半段,下一轮我们只在数组的前半段查找就可以了。
  • 如果中间的数字比 k,那么 k 只有可能出现在数组的后半段,下一轮我们只在数组的后半段查找就可以了。
  • 如果中间的数字和 k 相等,那么我们先判断这个数字是不是第一个 k,如果中间数字的前一个数字不是 k,那么此时中间的数字刚好就是第一个 k;如果中间数字的前一个数字也是 k,那么第一个 k 肯定在数组的前半段,下一轮我们仍然需要在数组的前半段查找;
  • 同理,查找最后一个目标值 k ,也是一样的操作。
class Solution {
    // Soultion1:二分查找
    public int search(int[] nums, int target) {
        // 定义变量 res,用来存储最后结果
        int res = 0;
        // 判断有效数组条件
        if (nums.length > 0) {
            // 找第一个和最后一个目标值
            int first = binarySearchFirst(nums,0,nums.length-1,target);
            int last = binarySearchLast(nums,0,nums.length-1,target);
            // System.out.printf("first=%d, last = %d",first,last);
            // 找到后,计算范围
            if (first > -1 && last > -1) res = last - first + 1;
        }
        // 最后,返回结果
        return res;
    }

    // 二分找第一个出现的数字
    public int binarySearchFirst(int[] arr, int l, int r, int k) {
        // 递归终止条件
        if (l > r) return -1;
        // 找出中点
        int mid = l + (r-l)/2;
        int midData = arr[mid];
        // 如果找到的中点,为目标值,判断它的前一个数是否也为目标值
        if (midData == k) { 
            // 不为k,找到了!
            if ((mid > 0 && arr[mid-1] != k) || mid == 0) return mid;
            // 为k,没找到,向前逼近,重新找
            else r = mid - 1;
        } 
        // 找到的中点大于目标值,向左半边继续查找
        else if (midData > k) r = mid - 1;
        // 找到的中点小于目标值,向右半边继续查找
        else l = mid + 1;
        
        return binarySearchFirst(arr,l,r,k);
    }

    // 二分找最后一个出现的数字
    public int binarySearchLast(int[] arr, int l, int r, int k) {
        // 递归终止条件
        if (l > r) return -1;
        // 找出中点
        int mid = l + (r-l)/2;
        int midData = arr[mid];
        // 如果找到的中点,为目标值,判断它的前一个数是否也为目标值
        if (midData == k) { 
            // 不为k,找到了!
            if ((mid < r && arr[mid+1] != k) || mid == r) return mid;
            // 为k,没找到,向后逼近,重新找
            else l = mid + 1;
        } 
        // 找到的中点大于目标值,向左半边继续查找
        else if (midData > k) r = mid - 1;
        // 找到的中点小于目标值,向右半边继续查找
        else l = mid + 1;
        
        return binarySearchLast(arr,l,r,k);
    }
    
}

在这里插入图片描述

3. 题目2:0 ~ n-1 中缺失的数字

题目链接:https://leetcode.cn/problems/que-shi-de-shu-zi-lcof/

3.1 题目介绍

一个长度为 n-1 的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围 0~n-1 之内。在范围 0~n-1 内的 n 个数字中有且只有一个数字不在该数组中,请找出这个数字。

【测试用例】:
示例1:

输入: [0,1,3]
输出: 2

示例2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

【条件约束】:

限制

  • 1 <= 数组长度 <= 10000

3.2 题解 – 二分 – O(logn)

时间复杂度O(1),空间复杂度O(n)

解题思路】:

  1. 数学计算:我们可以先用公式 n(n-1)/2 求出数字 0~n-1 的所有数字之和,记为 sn,接着求出数字中所有数字的累加和,记为 sum,此时,sn-sum 得到的差值,即为缺失的数字;
  2. 二分查找:因为 0~n-1 这些数字在数组中是排序的,因此数组中开始的一些数字与它们的下标相同,我们可以将那个缺失的数字记为 m,那么这个 m 正好就是数组中第一个数值和下标不相等的下标,这个问题就可以转换为如何在排序数组中找出第一个值和下标不相等的元素。【具体实现思路可参考题目1 中 求第一个目标值的过程】

1. 数学计算法:前 n-1 项和 减去 数组累加和,所产生的差即为缺少的数字。

class Solution {
    // Solution1:数学计算
    public int missingNumber(int[] nums) {
        // 获取数组长度
        int n = nums.length + 1;
        // 前 n-1 项和
        int sn = n*(n-1)/2;
        // 循环遍历数组和
        int sum = 0;
        for (int i : nums) {
            sum += i;
        }
        // 前n-1项和 与 数组和的差,即为缺少的数字
        return sn - sum;
    }
}

在这里插入图片描述
2. 二分查找:在排序数组中找出第一个值和下标不相等的元素。

class Solution {
    // Solution2:二分查找
    public int missingNumber(int[] nums) {
        // 无效输入判断
        if (nums.length <= 0) return -1;
        // 定义左、右边界
        int left = 0, right = nums.length-1;
        // 循环二分
        while (left <= right) {
            // 获取数组中间元素
            int mid = left + (right - left)/2;
            int midData = nums[mid];
            // 如果当前元素和下标相同,向右继续寻找
            if (midData == mid) left = mid + 1;
            // 如果当前元素和下标不同,判断其是否为第一个不同的元素
            else if (midData != mid) {
                // 是第一个不同元素,返回当前下标
                if ((mid > 0 && nums[mid-1] == mid-1) || mid == left) return mid;
                // 不是,则向左继续寻找
                else right = mid-1;
            }
        }
        // 为测试用例[0]准备的,[0]的答案是1
        if (left == nums.length) return nums.length;
        // 循环结束,没有返回,说明输入给定数据不符合条件
        return -1;

    }
}

在这里插入图片描述

4. 题目3:数组中数值和下标相等的元素

题目链接:https://www.acwing.com/problem/content/65/

4.1 题目介绍

假设一个 单调递增 的数组里的每个元素都是整数并且是 唯一 的。请编程实现一个函数找出数组中任意一个 数值等于其下标 的元素。例如,在数组 [−3,−1,1,3,5] 中,数字 3 和它的下标相等。

【测试用例】:
示例1:

输入:[-3, -1, 1, 3, 5]
输出:3

【条件约束】:

提示

  • 1 <= 数组长度 <= 100
  • 如果不存在,则返回-1。

4.2 题解 – 二分 – O(logn)

时间复杂度O(1),空间复杂度O(n)

解题思路】:

  1. 暴力枚举:从头到尾依次扫描数值中的数字,并逐一检验数字是不是和下标相等;
  2. 二分查找:查找数组值与下标相同的元素。

……
实现策略】:
二分的实现策略:

  • 如果 数组值和下标 相同,则直接返回下标;
  • 如果 数组值和下标 不同,分两种情况讨论:
    • 数组值大于下标,由于数组是单调递增的,则说明该值右边元素值也都大于下标,可以忽略,此时我们可以去左边的值进行寻找;
    • 数组值小于下标,同理,该值左边的元素在也都小于下标,忽略,此时去右边继续寻找。
class Solution {
    // Solution1:二分
    public int getNumberSameAsIndex(int[] nums) {
        // 无效输入判断
        int n = nums.length;
        if (n <= 0) return -1;
        // 定义边界
        int left = 0, right = n-1;
        // 循环二分
        while (left <= right) {
            // 获取当前的中间元素
            int mid = left + (right - left)/2;
            int midData = nums[mid];
            
            // 判断当前数是否等于下标,如果等于,则直接返回
            if (midData == mid) return mid;
            // 如果当前数大于下标,由于单调递增,则右边的数也都大于小标,所以应往左找
            else if (midData > mid) right = mid-1;
            // 如果当前数小于下标,应往右找
            else left = mid+1;
        }
        
        // 循环结束,没找着,返回-1
        return -1;
        
    }
}

在这里插入图片描述

5. 参考资料

[1] AcWing在线活动 – 感觉还蛮有意思的

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

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

相关文章

【创作赢红包】面向对象基础概念

什么是面向对象 面向对象&#xff08;Object Oriented&#xff09;是软件开发方法&#xff0c;一种编程范式&#xff0c;它是一种对现实世界理解和抽象的方法&#xff0c;是计算机编程技术发展到一定阶段后的产物。 早期的计算机编程是基于面向过程的方法&#xff0c;如实现算…

前端工程化:环境准备—NodeJs安装-Vue项目开发流程

一、前端工程化-Yapi 介绍&#xff1a;YApi 是高效、易用、功能强大的 api 管理平台&#xff0c;旨在为开发、产品、测试人员提供更优雅的接口管理服务 地址&#xff1a; http://yapi.smart-xwork.cn/ 具体操作步骤&#xff1a; 1、添加项目 2、添加接口&#xff1a;根据ID查…

技术分享 | observer 资源水位介绍

作者&#xff1a;郭斌斌 爱可生 DBA 团队成员&#xff0c;负责项目日常问题处理及公司平台问题排查。 本文来源&#xff1a;原创投稿 *爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。 OceanBase 集群界面会展示 Observer …

MATLAB绘制柱状/饼图填充图

MATLAB绘制柱状填充图方法1&#xff1a;hatchfill2工具1.1 案例1&#xff1a;柱状图填充1.2 案例2&#xff1a;饼图填充方法2&#xff1a;applyhatch函数2.1 案例1&#xff1a;柱状图填充2.2 案例2&#xff1a;饼图填充方法3&#xff1a; applyhatch_plusC函数3.1 案例1&#x…

Python 小型项目大全 31~35

三十一、猜数字 原文&#xff1a;http://inventwithpython.com/bigbookpython/project31.html 猜数字是初学者练习基本编程技术的经典游戏。在这个游戏中&#xff0c;电脑会想到一个介于 1 到 100 之间的随机数。玩家有 10 次机会猜出数字。每次猜中后&#xff0c;电脑会告诉玩…

Java实现ArrayList和底层源码讲解

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

Centos7安装MySQL5.7.30

文章目录1. 环境准备1.1 卸载mariadb1.2 下载MySQL 5.7.301.3 安装MySQL依赖项1.4 创建目录1.5 创建用户和用户组1.6 修改Mysql用户权限2. 安装MySQL2.1 解压2.2 修改解压目录名称2.3 初始化2.4 添加my.cnf异常找不到Sock文件2.5 启动MySQL服务2.5.1 建立软连接2.5.2 启动2.6 设…

TCP协议工作机制二(滑动窗口,流量控制,拥塞控制,延时应答,捎带应答等)

目录 滑动窗口 流量控制 拥塞控制 延时应答 捎带应答 面向字节流 异常情况 UDP和TCP对比 滑动窗口 由于TCP是可靠传输,有确认应答,超时重传,连接管理等机制,发送消息时需要等待接收方返回的ack.因此会消耗大量等待ack的时间,我们引入滑动窗口的机制来竭尽可能提高TCP的…

基于支持向量机的Digits手写数字识别

基于支持向量机的Digits手写数字识别 描述 支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;模型既可以用于分类也可以用于回归。手写数字识别是一个多分类问题&#xff08;判断一张手写数字图片是0~9中的哪一个&#xff09;&#xff0c;数据集…

图片英文翻译成中文转换器-中文翻译英文软件

您正在准备一份重要的英文资料或文件&#xff0c;但是您还不是很熟练地掌握英文&#xff0c;需要翻译才能完成您的任务吗&#xff1f;哪个软件能够免费把英文文档翻译成中文&#xff1f;让我们带您了解如何使用我们的翻译软件来免费翻译英文文档为中文。 我们的翻译软件是一款功…

C风格的字符串赋值方式

文章目录&#xff08;1&#xff09;C语言中&#xff0c;没有字符串类型但可以用字符数组模拟字符串。&#xff08;2&#xff09;C语言中&#xff0c;字符串是以’\0’作结尾字符。&#xff08;3&#xff09;C语言中&#xff0c;字符串常量本质上是一个无名的字符数组。C风格的字…

使用Spring JDBC中的JdbcTemplate对数据进行增删改查操作教程~

jdbcTemplate实现添加数据功能&#xff1a; spring框架对jdbc框架进行封装&#xff0c;使用jdbcTemplate方便实现对数据库的操作 数据库准备工作&#xff1a; 在已有数据库中创建新的表&#xff1a; create table t_user (id int,username varchar(20),password varchar(20…

搜索词分析工具-网站关键词挖掘

怎么能找到行业的关键词 以下是如何找到行业关键词的建议&#xff1a; 了解行业&#xff1a;要找到与行业相关的关键词&#xff0c;首先需要了解行业。了解行业以及核心目标&#xff0c;从而更好地理解行业中的主题和词汇。 找到竞争对手网站&#xff1a;搜索竞争对手的网站&…

k8s部署Dashboard

k8s和Dashboard的版本对应关系可以到Dashbord的对应版本里看&#xff0c;比如这里&#xff1a; https://github.com/kubernetes/dashboard/releases/tag/v2.7.0 以下步骤都是在master上执行的。 1. 部署步骤 1. 获取Dashbord的yaml文件 wget https://raw.githubusercontent…

【Git】—— 如何安装Git及简单使用

Git是一个开源的分布式版本控制工具&#xff0c;可以更好地管理你的项目。 一、Linux操作系统 如果用的是Ubuntu系统&#xff0c;只需打开shell界面&#xff0c;输入&#xff1a; sudo apt-get install git-core 按下回车即可完成安装。 二、Windows操作系统 Windows操作系统不…

C语言-数据结构与算法-详细全面的链表知识总结归纳

C语言链式存储结构的详细讲解一.前言(为什么要使用链式存储)一.单链表1.单链表的结点描述2.单链表基本操作(1)初始化单链表(2)采用头插法建立单链表(带头结点)(3).采用尾插法建立单链表(4)按照位序查找结点(4)在链表中间插入结点(5)删除第i个结点二.双链表1.双链表的结点类型描…

和ChatGPT-4聊完后,我觉得一切可能已经来不及了

了然无味&#xff0c;晴空万里&#xff01;和ChatGPT-4开始了一场坦诚的沟通&#xff0c;它全程都表现出高情商&#xff0c;以及不断尽量安抚我的情绪&#xff0c;而这&#xff0c;恰恰令我脊背发凉。 部分文字截取 ZM&#xff1a;我能不能理解每次对话就是一次你的“生命” G&…

【Android -- 软技能】分享一个学习方法

前言 很多人都想通过学习来提升自己&#xff0c;但是&#xff0c;可能因为两个问题&#xff0c;阻碍了自己的高效提升&#xff1a; 学什么&#xff1f; 怎么学&#xff1f; 本文将从自己的学习实践出发&#xff0c;针对这两个问题&#xff0c;给出自己的一套学习流程。 1…

免费集装箱号识别API免费集装箱信息识别,中国人工智能企业CIMCAI集装箱识别云服务全球4千企业用户,中国人工智能企业智慧港航

免费集装箱号识别API免费集装箱信息识别API&#xff0c;CIMCAI飞瞳引擎™集装箱人工智能平台全球4千企业用户&#xff0c;全球领先的飞瞳引擎™AI集装箱识别云服务&#xff0c;集装箱残损识别箱况检测缺陷检验&#xff0c;小程序拍照检测或支持API接口二次开发&#xff0c;应用…

00后整顿职场,我直呼太卷了....

内卷的来源 内卷最早的“出处”是几张名校学霸的图片。 大学生们刷爆朋友圈的几张“内卷”图片是这样的&#xff1a;有的人骑在自行车上看书&#xff0c;有的人宿舍床上铺满了一摞摞的书&#xff0c;有的人甚至边骑车边端着电脑写论文。这些图片最早在清华北大的学霸之间流传。…