数组中只出现一次的两个数字

数组中只出现一次的两个数字

  • 背景
  • 题目描述
  • 题解

背景

刷到此题的时候,只写出了最普通的解法,最后看了二进制解法,叹为观止,不禁感叹到它的巧妙,因此记录一下,共勉。

题目描述

牛客地址:

https://www.nowcoder.com/practice/389fc1c3d3be4479a154f63f495abff8

描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

数据范围:
数组长度 2≤n≤1000,
数组中每个数的大小 0<val≤1000000

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

提示:输出时按非降序排列。

示例1
输入:[1,4,1,6]

返回值:[4,6]
说明:
返回的结果中较小的数排在前面

示例2
输入:[1,2,3,3,2,9]

返回值:[1,9]

题解

方法一:
题目给的意思分析之后,很容易想到一种方法,就是用哈希表进行统计,辅助得到这两个只出现一次的数字。
此方法比较简单,直接给出代码:

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param nums int整型一维数组 * @return int整型一维数组*/public int[] FindNumsAppearOnce (int[] nums) {Map<Integer,Integer> numMap = new HashMap();for(int i=0;i<nums.length;i++){Integer num = numMap.get(nums[i]);if(null == num){numMap.put(nums[i],1);}else{numMap.put(nums[i],2);}}ArrayList<Integer> res = new ArrayList<Integer>();Set<Integer> numKeySet = numMap.keySet();for(Integer numKey:numKeySet){if(numMap.get(numKey) == 1){res.add(numKey);}}Collections.sort(res);int[] intRes = new int[res.size()];for(int i=0;i<res.size();i++){intRes[i] = res.get(i);}return intRes;}
}

本文主要对第二种解法的思路做一下整理和分析。

对于这道题目,我们先来想另外一个问题:

如果数组中只有一个出现了一次的数字,我们想到得到它,那么应该如何解决呢?

我们都知道异或运算:如果两个数一样则异或结果为0,不一样则异或结果为1。(二进制)

(0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0)

举个例子:

4 ⊕ 4 = 0,将4化为二进制为 0100

所以 0100

异或 0100

得到 0000

4 ⊕ 4 ⊕ 5 = 5


​ 0100

​ 0100

​ 0101

得到 0101

我们可以看到上面的运算过程,因为4=4,两者相等异或结果为0。所以0异或任意数都等于任意数。

所以,当只有一个出现了一次的数字的时候,则只需要将全部数进行异或运算,运算结果就剩下了那个只出现一次的数字了。

public int[] singleNumber(int[] nums) {int x = 0;for(int num : nums)  // 1. 遍历 nums 执行异或运算x ^= num;return x;            // 2. 返回出现一次的数字 x
}

好了,上面说了这么多,那这道题目是找两个只出现一次的数字呀~

上面的方法又是针对只出现一次的数字,假设我也一样全部执行异或运算 1⊕4⊕1⊕6,最后也还是会剩下4⊕6呀~

我们看看:

0100 ⊕ 0110 = 0010 这个结果也不能得出什么东西哇~

我们换个角度思考,能不能做个分组,将题目分为两组 ,然后每一组求出其中的出现一次的数字,最后两者一起返回,不就解决问题了吗?

那么我们要如何分组呢?位运算进行分组,我们首先想到的应该是奇偶分组,就是将所有数 &1,此时能将数字分为奇偶两组。

但是这个时候问题又来了,你又不能保证两个数字就一奇一偶,有可能都是奇数也有可能都是偶数呀~

但是,我们想一下,&1的操作,归根到底,是按照二进制最低位的不同来分组的,
例如 : 0011(3) ,0101(5),0100(4),0001(1)
对上面四个数分组,我们都&1,可以分得结果: 0011,0101,0001(奇数) 0100 (偶数)
我们很明显能够知道,当二进制&1结果为1的时候,为奇数,反之为偶数。它们是按照最低位的不同来分组的。

上面我们知道,能够将数字分为 奇偶两组,那么现在,我再给出一个难度,如何区分出 0011,0101 ?
对 0011,0101 这两个数进行分组,我们可以观察到最低位都为1,此时如果我们还是进行&1操作去分组,那肯定是分不出来的!
因为两数的最低为都是一样的,&1之后还是1,还是无法区分,那么我们看到最低的第二位0011是1,0101是0,很明显这两位就不一样,那么我们就可以将这两数&0010呀,不就能够区分出来了吗?
0011 &0010 = 0010 0101&0010 = 0000,此时还是根据结果是否为0得到分组!
那要是是 0100 和 1100呢?如何分组呢? 不就是&1000 就能够分组了吗?

所以,说了那么多,其实就是为了推出一个分组的方式,两个不同的数如何分组!
我们都知道两个不同的数,那么它的二进制表示肯定是不一样的!这是毋庸置疑的!

所以,我们要想对两者进行分组操作,就是需要找到两者中的那一位不同的二进制,然后得到分组的与值(去&的那个值),问题不就解决了吗?

那要怎么找到那一位不同的二进制呢?
我们看一个例子: 1,1,4,6
全部做异或运算结果为 4⊕6 = 0100⊕0110 = 0010
异或的运算规则是什么? 相同的为0,不同的为1。所以我们根据两者异或出来的结果 0010,不就可以知道那一位不同了嘛?(为1的那一位就是不同的)
好了,说了这么多,下面安排代码把~

    public int[] FindNumsAppearOnce (int[] nums) {//定义一个异或的值int xorRes = 0;//求出所有数字的异或值for(int num:nums){xorRes^=num;   }//定义分组掩码,0为1组,1为1组,正好2组int mask = 1;//寻找分组的掩码位,用来分组用,掩码只能有一位是1,否则后面//进行与运算可能无法分出来,因为多位的话与运算可能结果都不为0//只有一位为1掩码,才能确保其中一个数字结果一定为0while((mask&xorRes) == 0){mask = mask<<1;}int a=0,b=0;for(int num:nums){//如果与掩码做与运算等于0的分到第一组if((mask&num) == 0){a^=num;}//否则是第二组else{b^=num;}}if(a>b){return new int[]{b,a};}else{return new int[]{a,b};}}

复杂度分析:
时间复杂度:O(N)。数组的长度n,循环。
空间复杂度:O(1)。几个变量的空间。

备注:大部分文字转载自牛客精华题解,有兴趣的可以去看一下。

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

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

相关文章

【生态适配】亚信安慧AntDB数据库与FT-2000+/64处理器完成兼容互认

日前&#xff0c;亚信安慧AntDB数据库完成了与FT-2000/64处理器的兼容互认。经湖南亚信安慧科技有限公司&#xff08;简称“亚信安慧”&#xff09;与飞腾信息技术有限公司&#xff08;简称“飞腾公司”&#xff09;的严格测试&#xff0c;亚信安慧AntDB数据库V6.2在FT-2000/64…

Android Activity启动模式详解

在Android开发中&#xff0c;Activity的启动模式对于应用程序的行为和用户体验至关重要。Android提供了五种不同的启动模式&#xff0c;它们分别是&#xff1a;standard、singleTop、singleTask、singleInstance和singleInstancePerTask。每种启动模式都有其特定的行为和用途。…

备战蓝桥杯————前缀和数组1

一、基本概念 定义&#xff1a;前缀和数组&#xff08;Prefix Sum Array&#xff09;是一个数组&#xff0c;它存储了原数组&#xff08;或序列&#xff09;的连续子数组之和。对于一个给定的数组 nums&#xff0c;其前缀和数组 cumulativeSum 的第 i 个元素 cumulativeSum[…

【前端素材】推荐优质在线家具电商网页Hurst平台模板(附源码)

一、需求分析 1、系统定义 家具电商平台是指专门销售家具产品的在线电子商务平台。这些平台专注于家具类商品的销售和服务&#xff0c;为消费者提供方便快捷的购买体验。 2、功能需求 在线家具商城平台是指通过互联网提供家具购买服务的电子商务平台。这类平台为消费者和家…

基于ESP32的MicroPython项目量产烧写指南

背景 前段时间用MicroPython开发了一个项目&#xff0c;硬件是ESP32-C3&#xff0c;目前准备量产&#xff0c;我需要提供固件以供加工厂批量烧录&#xff0c;需要把我有程序的板子里的程序读出来&#xff0c;然后下到别的板子上&#xff0c;以下做这件事情的过程记录。 1.固件…

专利实缴评估

专利实缴评估通常是指在企业进行注册资本实缴时&#xff0c;将专利权作为出资的形式&#xff0c;需要对专利权的价值进行评估&#xff0c;以确定其作为注册资本的数额。 这项评估过程主要涉及以下几个步骤&#xff1a; 1.专利权确认&#xff1a; 首先&#xff0c;需要明确待评…

专利转让具体怎么做

专利转让是指专利权人将自己的专利权全部或部分转移给他人的法律行为。在中国&#xff0c;专利转让的流程一般包括以下几个步骤&#xff1a; 1.签订转让合同&#xff1a;•专利权人&#xff08;转让方&#xff09;与受让人协商一致后&#xff0c;需签订书面的专利权转让合同。…

3_相机模型

相机标定对于联系相机测量和真实三维世界测量也很重要。它的重要性在于场景不仅仅是三维的&#xff0c;也是物理单位度量的空间。因此&#xff0c;确定相机的自然单位(像素)与物理单位(如mm)的关系是三维场景重构的重要部分。相机标定的过程既给出相机的几何模型又给出透镜的畸…

电感器在电路中的作用

同学们大家好&#xff0c;今天我们继续学习杨欣的《电子设计从零开始》&#xff0c;这本书从基本原理出发&#xff0c;知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例&#xff0c;可以说是全面系统地介绍了电子设计所需的知识…

pause、alarm、kill——进程——day13

今天主要讲线程中信号的三个函数 1.pause 1.pauseint pause(void); 功能:让进程睡眠,直到接收到信号(捕捉)才能继续向下执行eg&#xff1a; #include "head.h"void handler(int signo) {return; }int main(void) {signal(SIGINT,handler);printf("pause up!\…

学习大语言模型(LLM),从这里开始

在见识了ChatGPT的各种强大能力后&#xff0c;不少 NLP一线从业人员很自然地想到&#xff0c;以后开发者只要借助 ChatGPT&#xff0c;就可以做到现在大部分NLP工程师在做的事&#xff0c;比如文本分类、实体抽取、文本推理等。甚至随着大语言模型&#xff08;largelanguagemod…

2 开源鸿蒙OpenHarmony4.1源码下载和编译流程

开源鸿蒙OpenHarmony源码下载和编译流程 作者将狼才鲸日期2024-02-29 前景提要&#xff1a; 确定你要编译哪一块开发板&#xff1a; 我选择qemu_mini_system_demo 这个板子编译形态整体说明&#xff0c;含QEMU&#xff0c;可以编译哪些开发板和芯片qemu模拟器 arm核 mini轻…

Keepalived 双机热备基础知识

7.1 Keepalived 双机热备基础知识 Keepalived起初是专门针对LVS设计的一款强大的辅助工具&#xff0c;主要用来提供故障切换(Failover) 和健康检查査(Health Checking)功能一一判断LVS 负载调度器、节点服务器的可用性&#xff0c;及时隔离并替 换为新的服务器&#xff0c;当故…

Container killed on request. Exit code is 143

Bug信息 WARN YarnAllocator: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143 Container exited with a non-zero exit code 143 Killed by externa…

day03_登录注销(前端接入登录,异常处理, 图片验证码,获取用户信息接口,退出功能)

文章目录 1. 前端接入登录1.1 修改前端代码1.2 跨域请求1.2.1 跨域请求简介1.2.2 COSR概述CORS简介CORS原理 1.2.3 CORS解决跨域 2. 异常处理2.1 提示空消息分析2.2 系统异常分类2.3 异常处理2.2.1 方案一2.2.2 方案二 3. 图片验证码3.1 图片验证码意义3.2 实现思路3.3 后端接口…

java找工作之JavaWeb(一)

JavaWeb 一个web应用有多部份组成&#xff08;静态web&#xff0c;动态web&#xff09; html&#xff0c;css&#xff0c;jsjsp&#xff0c;servletjava程序jar包配置文件(Properties) web应用程序编写完毕后&#xff0c;若想提供给外界访问&#xff0c;需要一个服务器来统一…

【IC验证】数据类型

一、思维导图 二、代码 &#xff08;1&#xff09;字符串 字符串的结尾不带空标识符null&#xff0c;\0。是动态存储方式&#xff0c;因此不用担心存储空间被用完。 编辑.SV文件使用 gvim 文件名 运行仿真使用 vcs -sverilog -full64 string_type.sv 和./simv命令如果是字…

【微服务】微服务架构包含哪些组件、各个组件又承担什么职责呢?

前面的文章里我们介绍了微服务架构的一些基础知识以及特点&#xff0c;也提到了国产的微服务架构标准实现SpringCloud Alibaba&#xff0c;聊了这么多&#xff0c;还是没有开始阐述各组件在Java技术体系中的具体实现。那从这里开始&#xff0c;我们开始全面认识微服务架构在Jav…

vue实现图片上传至oss,返回url插入数据库,最后在前端页面上回显图片

vue前端上传图像 写一个弹窗上传图片 上传图片的方式是oss&#xff0c;在上传之后将url存入数据库&#xff0c;下一步则是在列表上回显 <template><el-dialog:title"新增":visible.sync"dialogVisible":close-on-click-modal"false"…

不同类型的工业网关有何区别?

工业网关是一种用于连接工业设备和网络的关键设备&#xff0c;它能够将不同协议、不同传输速率的工业设备连接到网络上&#xff0c;实现数据的传输和共享。不同类型的工业网关之间存在一些区别&#xff0c;以下是一些常见的工业网关类型及其区别&#xff1a; 协议转换型工业网关…