【LeetCode】剑指 Offer 56. 数组中数字出现的次数 p275 -- Java Version

news2024/11/26 0:46:51

1. 题目介绍(56. 数组中数字出现的次数)

面试题56.:数组中数字出现的次数, 一共分为两小题:

  • 题目一:数组中只出现一次的两个数字
  • 题目二:数组中唯一只出现一次的数字

2. 题目1:数组中只出现一次的两个数字

题目链接:https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/

2.1 题目介绍

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是 O(n),空间复杂度是 O(1)

【测试用例】:
示例1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

示例2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

【条件约束】:

限制

  • 2 <= nums.length <= 10000

2.2 题解 – 位运算(XOR)-- O(n)

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

解题思路】:
该问题是问在一个数组中找出 两个只出现一次的数字 ,那么我们可以先从这个数组中 只有一个数字只出现了一次 开始考虑:

  • 首先我们可以想到 异或运算 的一个性质:任何一个数字异或它自己都等于0;也就是说,如果我们从头到尾依次异或数组中的每个数字,那么最终的结果刚好是那个只出现依次的数组,因为那些成对出现两次的数字全部在异或中抵消了(当然,这也是一个前提条件,要求 数组中的其它数字都是出现了两次,而不是三次或其他次,只能是 偶数次 才行)

……
既然,我们有了得到只出现一次数字的办法,那么我们就要想怎么求出两个只出现一次的数字:

  • 我们可以试着将 原数组分成两个子数组 ,使得每个子数组包含一个只出现一次的数字,而其它数字都承兑出现两次。只要能够这样拆分成两个数组,那么我们就可以按照前面的办法分别找出两个只出现一次的数字

……
实现策略】:

  1. 首先还是先进行无效输入的判断,判断数组长度 nums.length 是否小于等于 0,如果是,则说明是无效数据;
  2. 定义变量 exclusiveOr,获取数组中所有元素的异或结果(该结果可以等同于 数组中两个唯一只出现一次的数字 的异或结果);
  3. 我们可以根据这个异或结果(exclusiveOr),去寻找这两个数字是在哪一位开始不同的,即从低位到高位,第一个 Bit为1 的位置;
  4. 找到这个位置后,我们就可以根据这个位置进行分组了,我们将 该位为1 的数据分为一组,不为1 的分为一组,然后对其进行异或,最后剩下的数字就是我们要找到的两个数字了。
class Solution {
    // Solution1:异或分组和筛选
    public int[] singleNumbers(int[] nums) {
        // 无效输入判断
        if (nums.length <= 0) return null;
        // 将数组中所有元素进行异或
        int exclusiveOr = 0;
        for (int i = 0; i< nums.length; i++) {
            // 异或完成后,一样的会被抵消,只剩下不一样的两个数字,需要我们对其进行分组
            exclusiveOr ^= nums[i];
        }
        int indexBitIs1 = findFirstBitIs1(exclusiveOr);
        // 遍历数组并分组判断
        int[] res = new int[2];
        for (int j = 0; j < nums.length; j++) {
            if (isBit1(nums[j], indexBitIs1)) res[0] ^= nums[j];
            else res[1] ^= nums[j];
        }
        // 循环结束,返回结果
        return res;

    }
    // 从右向左寻找第一位为1的位数
    public int findFirstBitIs1(int num) {
        int indexFirstBitIs1 = 0;
        while ((num & 1) == 0 && (indexFirstBitIs1 < 32)) {
            num = num >> 1;
            indexFirstBitIs1++;
        }
        return indexFirstBitIs1;
    }
    // 判断输入的数字的indexBitIs1位是不是1
    public boolean isBit1(int num, int indexBitIs1) {
        num = num >> indexBitIs1;
        return (num & 1) != 0 ;
    }
}

代码简化:

class Solution {
    public int[] singleNumbers(int[] nums) {
        int x = 0, y = 0, n = 0, m = 1;
        for(int num : nums)               // 1. 遍历异或
            n ^= num;
        while((n & m) == 0)               // 2. 循环左移,计算 m
            m <<= 1;
        for(int num: nums) {              // 3. 遍历 nums 分组
            if((num & m) != 0) x ^= num;  // 4. 当 num & m != 0
            else y ^= num;                // 4. 当 num & m == 0
        }
        return new int[] {x, y};          // 5. 返回出现一次的数字
    }
}

3. 题目2:

题目链接:https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/

3.1 题目介绍

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

【测试用例】:
示例1:

输入:nums = [3,4,3,3]
输出:4

示例2:

输入:nums = [9,1,7,9,7,9,7]
输出:1

【条件约束】:

限制

  • 1 <= nums.length <= 10000
  • 1 <= nums[i] < 2^31

3.2 题解 – 位运算 – O(n)

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

解题思路】:
因为三个相同的数字的异或结果还是该数字,因此我们在这里就不能再使用异或运算解决该问题了,不过我们还是可以沿用位运算的思路:

  1. 如果一个数字出现三次,那么它的二进制表示的每一位(0 或者 1)也出现三次;
  2. 如果把所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被 3 整除;
  3. 我们把数组中所有数字的二进制表示的每一位都加起来,如果某一位的和能被 3 整除,那么那个只出现一次的数字二进制表示中对应的那一位是 0;否则就是 1.

……
这种解法的时间效率是 O(n),我们需要一个长度为 32 的辅助数组存储二进制表示的每一位的和。当然,除此之外,还有其它解法:

  1. 排序数组中找到只出现一次的数字,但排序需要额外的 O(nlogn) 的时间;
  2. 用一个哈希表来记录数组中每个数字出现的次数,但这个哈希表需要额外的 O(n) 的空间;
  3. 有限状态自动机:各二进制位的 位运算规则相同 ,因此只需考虑一位即可。如下图所示,对于所有数字中的某二进制位 1 的个数,存在 3 种状态,即对 3 余数为 0,1,2 ;该方法虽然效率较高,但也较难理解
class Solution {
    // Solution1:位运算
    public int singleNumber(int[] nums) {
        // 定义辅助数组 counts,用来存储二进制表示的每一位的和
        int[] counts = new int[32];
        // 循环遍历数组中的所有数
        for(int num : nums) {
            // 将该数的32位分别存入数组
            for(int j = 0; j < 32; j++) {
                counts[j] += num & 1;
                num = num >> 1;
            }
        }
        int res = 0, m = 3;
        for(int i = 0; i < 32; i++) {
            // 移位处理
            res <<= 1;
            // 如果 某一位的和能被 3 整除,那么那个只出现一次的数字
            // 二进制表示中对应的哪一位是0;否则就是1
            res |= counts[31 - i] % m;
        }
        return res;
    }
}

有限状态自动机:
在这里插入图片描述

class Solution {
    public int singleNumber(int[] nums) {
        int ones = 0, twos = 0;
        for(int num : nums){
            ones = ones ^ num & ~twos;
            twos = twos ^ num & ~ones;
        }
        return ones;
    }
}

4. 参考资料

[1] 剑指 Offer 56 - I. 数组中数字出现的次数(位运算,清晰图解)

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

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

相关文章

学术速运|利用深度学习和分子动力学模拟设计抗菌肽

题目&#xff1a; Designing antimicrobial peptides using deep learning and molecular dynamic simulations 文献来源:Briefings in Bioinformatics, 2023, 1–13 代码&#xff1a;https://github.com/gc-js/Antimicrobial-peptide-generation 简介&#xff1a;随着多药耐…

Springboot实现文件断点续传-基于GridFS

Springboot实现文件断点续传-基于GridFS 需求介绍 我们后台是使用GridFS存储文件对象的&#xff0c;之前客户端都是Web浏览器&#xff0c;网络环境相对较为稳定&#xff0c;所以我们直接提供文件下载就行。但最近新增需求需要在移动端进行文件下载&#xff0c;这就有问题了。…

c++函数重载

C函数重载&#xff08;Function Overloading&#xff09;是指在同一个作用域&#xff0c;可以定义多个名称相同但参数列表不同的函数。在调用这些同名函数时&#xff0c;编译器根据实参与各个形参的类型、个数或顺序等特征来确定调用哪一个函数。 通过函数重载&#xff0c;我们…

UE4/5多人游戏详解(三、创建会话,委托绑定回调函数)

目录 基础的创建 回调函数绑定到委托&#xff1a; 实现创建会话的函数createGameSession&#xff1a; 回调函数实现判断验证是否成功&#xff1a; 添加会话设置&#xff1a; 测试 基础的创建 [提示&#xff1a;中途如果有无法编译则删除Binaries,saved,Intermediate后重…

倍增?最近公共祖先?——从定义到实现,帮你一步步吃掉它!

倍增&#xff1f;最近公共祖先&#xff1f;——从定义到实现&#xff0c;帮你一步步吃掉它&#xff01; 一、倍增倍增——翻倍的增长 倍增是一种思想&#xff0c;实际上的操作就是通过不断翻倍来缩短我们的处理时间&#xff1a; 它可以把线性级别的处理优化到指数级。 举个…

5.redis-哨兵模式

01-哨兵模式概述 如果master宕机, 我们该怎么办? ①关闭所有slave②选举新的master, 建立新的主从结构 存在的问题 ①关闭期间, 谁来提供数据服务②选举新master的标准是什么③原来的master恢复了怎么办 哨兵模式 sentinel是一个分布式系统&#xff0c;用于对主从结构中的每…

【多线程】Thread类

1. Java中如何进行多线程编程&#xff1f;线程是操作系统中的概念&#xff0c;操作系统内核实现了线程这样的机制&#xff0c;并且对用户层提供了一些 API 供用户使用(如 Linux 中的 pthread 库)。所以本身关于线程的操作&#xff0c;是依赖操作系统提供的的 API&#xff0c;而…

练习,异常,异常处理,try-catch,throws

package com.jshedu.homework_;/*** author Mr.jia* version 1.0*/ //匿名内部类 public class Homework04 {public static void main(String[] args) {Cellphone cellphone new Cellphone();//1.匿名内部类&#xff0c;同时也是一个对象/*new computer() {Overridepublic dou…

JavaClient With HDFS

序言 在使用Java创建连接HDFS的客户端时,可以设置很多参数,具体有哪些参数呢,只要是在部署HDFS服务中可以设置的参数,都是可以在连接的时候设置. 我没有去验证所有的配置是否都可以验证,只是推测cuiyaonan2000163.com 依据 创建HDFS的构造函数如下所示: 网上比较常用的是get…

gdb 跟踪调式core

自己编译的问题出现段错误: 编译:使用gdb调试core文件来查找程序中出现段错误的位置时,要注意的是可执行程序在编译的时候需要加上-g编译命令选项。 gdb调试core文件的步骤 gdb调试core文件的步骤常见的有如下几种,推荐第一种。 具体步骤一: (1)启动gdb,进入core文…

【剑指 offer】旋转数组的最小数字

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;算法训练营&#x1f447; 旋 转 数 组 的 最 小 数 字核心考点&#xff1a;数组理解&#xff0c;二分查找&#xff0c;临界条件 描述&#xff1a; 有一个长度为 n 的非降序数组&#xff0c;比如[1,2,3,4,5]…

ABAP 创建、修改、删除内部交货单(VL31N/VL32N)

一、干货 VL31N创建的BAPI&#xff1a; 1.GN_DELIVERY_CREATE 通用交货单使用的bapi&#xff0c;推荐使用 2.BAPI_DELIVERYPROCESSING_EXEC 简单&#xff0c;但是字段比较少 3.BBP_INB_DELIVERY_CREATE 听说有bug&#xff0c;我就没有使用这个了 VL32N修改/删除BAPI: BAPI_INB…

每日学术速递4.14

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Deep RL at Scale: Sorting Waste in Office Buildings with a Fleet of Mobile Manipulators 标题&#xff1a;大规模深度强化学习&#xff1a;使用移动机械手对办公楼中的垃圾进行…

VS2022编译libui库

libui是一个 C 中简单且可移植(但并非不灵活)的 GUI 库,它使用每个平台原生的GUI技术进行绘制。 官网地址:链接 本文将使用VS2022编译libui库,操作系统为Windows10。 1. 下载源代码 首先在官网下载源代码,由于此代码不依赖第三库,故只需下载源代码即可进行编译。 我下…

R730服务器环境搭建(centos7、lanproxy、docker、k8s)

文章目录前言一、centos7安装1.制作u盘启动盘2.开始装系统&#xff1a;二、环境安装&#xff08;lanproxy、docker、k8s&#xff09;1.lanproxy安装2.docker安装&#xff08;如果通过k8sOfflineSetup安装k8s可以跳过这一步&#xff0c;因为会自动安装docker&#xff09;3.安装k…

安装 KeyShot 流程

| 安装 KeyShot 流程 KeyShot 安装程序将指导您完成安装过程。 在 Windows 上&#xff0c;安装过程会要求您考虑以下事项终用户协议 为使用计算机的所有人或仅为当前用户安装 KeyShot 安装文件夹的位置 资源文件夹的位置 ——资源文件夹包含许多可以与 KeyShot 一起使用的纹…

NSSCTF doublegame题解

运行一下&#xff0c;是一个贪吃蛇游戏 先玩一玩&#xff0c;蛇的移动速度太快了&#xff0c;玩不了 查壳 64位文件&#xff0c;无壳 进入IDA分析 发现这个EXE文件是开了程序基址随机化&#xff0c;就是每次用IDA打开指令的地址不一样 我们要想使用x64dbg和IDA的时候&#…

Docker的基本操作

文章目录一、 Docker的基本操作1.1 镜像1.1.1 介绍1.1.2 镜像操作1.2 容器1.2.1 介绍1.2.2 容器操作1.3 数据卷1.3 介绍1.3.2 数据卷操作一、 Docker的基本操作 1.1 镜像 1.1.1 介绍 在 Docker 中&#xff0c;镜像&#xff08;Image&#xff09;是一种轻量级、可移植的、可扩…

营销平台一站式集成 高效实现自动化

市面上广告投放渠道渠道那么多&#xff0c;图文、动图、短视频等广告形式也越来越多&#xff0c;许多企业都会有这些疑问&#xff1a; 「腾讯广告、百度营销、巨量引擎哪个广告渠道的客户适合我们公司&#xff1f;」 「这么多广告渠道&#xff0c;哪家的点击率、转化率比较高…

1.Antlr4-简介入门

1.简介: ANTLR v4是一款功能强大的语法分析器生成器&#xff0c;可以用来读取、处理、执行和转换结构化文本或二进制文件。它被广泛应用于学术界和工业界构建各种语言、工具和框架。 2 关键字&#xff1a; import, fragment, lexer, parser, grammar, returns, locals, throw…