首页 > 其他分享 >【剑指Offer】40、数组中只出现一次的数字

【剑指Offer】40、数组中只出现一次的数字

时间:2023-06-23 23:44:06浏览次数:52  
标签:一次 数字 Offer 40 异或 数组 出现 num1

【剑指Offer】40、数组中只出现一次的数字

题目描述:

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

解题思路:

这道题目相对比较难,一般情况下,我们首先可以想到的是顺序扫描数组,但其时间复杂度为O(n^2)。进一步也可以想到用哈希表保存每一个数次出现的次数,但是这使用了辅助空间,空间复杂度为O(n)。显然均不满足题目要求。

我们先来看一个比较简单的情况,如果数组中只有一个数字出现一次,其他都出现两次。那么我们应该可以想到异或运算。异或运算有一个比较好的性质是:相同为0,相异为1。也就是说,任何一个数字异或它自己都等于0,而0异或任何数都等于那个数。因此,我们从头到尾依次异或数组中的每个数字,那么最终结果刚好是那个只出现一次的数字,重复的数字在异或过程中被抵消了。

这是一种比较巧妙的思路,然而,本题只出现一次的数字有两个,简单的异或无法解决。但是,借助这种思路,我们可以进一步分析,如果我们能把数组分成两个子数组,使每个子数组包含一个只出现一次的数字,而其他数字成对出现,那么我们通过上述解法就可以找到两个元素。

具体思路是:我们首先仍然从前向后依次异或数组中的数字,那么得到的结果是两个只出现一次的数字的异或结果,其他成对出现的数字被抵消了。由于这两个数字不同,所以异或结果肯定不为0,也就是这个异或结果一定至少有一位是1,我们在结果中找到第一个为1的位的位置,记为第n位。接下来,以第n位是不是1为标准,将数组分为两个子数组,第一个数组中第n位都是1,第二个数组中第n位都是0。这样,便实现了我们的目标。最后,两个子数组分别异或则可以找到只出现一次的数字。

举例:

以{2,4,3,6,3,2,5,5}为例:

我们依次对数组中的每个数字做异或运行之后,得到的结果用二进制表示是0010。异或得到结果中的倒数第二位是1,于是我们根据数字的倒数第二位是不是1分为两个子数组。第一个子数组{2,3,6,3,2}中所有数字的倒数第二位都是1,而第二个子数组{4,5,5}中所有数字的倒数第二位都是0。接下来只要分别两个子数组求异或,就能找到第一个子数组中只出现一次的数字是6,而第二个子数组中只出现一次的数字是4。

编程实现(Java):

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        /*
        思路:数组中的元素先依次异或,相同为0,则得到的是两个只出现一次的数的异或结果
        对于得到的异或结果,找到其第一个为1的位
        该位为1,说明两个只出现一次的数该位不同,所以按照该位是0还是1将数组分成两部分
        这样,出现两次的数字都会分到同一个部分,而两个只出现一次的数正好被分开,再各自异或可得结果
        */
        if(array==null||array.length<2)
            return ;
        int res=0;
        for(int num:array) //数组中的元素先依次异或,相同为0,则得到的是两个只出现一次的数的异或结果
            res ^= num;
        
        int index=0; //找到其第一个为1的位
        for(;index<32;index++){
            if(((res>>index) & 1)==1)
                break;
        }
        
        num1[0]=0;
        num2[0]=0;
        for(int num:array){ //按照该位是0还是1将数组分成两部分,分别异或
            if(((num>>index)&1)==1)
                num1[0] ^= num;
            else
                num2[0] ^= num;
        }
    }
}

标签:一次,数字,Offer,40,异或,数组,出现,num1
From: https://www.cnblogs.com/bujidao1128/p/17500511.html

相关文章

  • 二维数组的传参方式
    1.传数组整体格式:(1)给函数传参时,用数组名arr;(2)函数定义时,用intarr[3][5]接收;(3)在函数中打印元素时,用arr[1][1]。voidtest(intarr[3][5]){ printf("%d",arr[1][1]);}intmain(){ intarr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}}; test(arr); return0;}2.传首元......
  • 3.6万亿token、3400亿参数,谷歌大模型PaLM 2细节遭曝光
    谷歌内部文件又泄露了,这次是谷歌新一代大模型PaLM2的训练细节:训练数据量是前代的近5倍、参数量是前代的三分之二左右。上周四,在2023谷歌I/O大会上,谷歌CEO皮查伊宣布推出对标GPT-4的大模型PaLM 2,并正式发布预览版本,改进了数学、代码、推理、多语言翻译和自然语言生成......
  • 【js学习笔记三】数组去重的第二种方式indexof
     目录前言导语代码部分 运行结果总结前言   我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷导语......
  • 840个最优的机器学习python开源项目整理分享
    本资源包含了840个很棒的机器学习开源项目,总共270万颗星分为32个类别。所有项目均按项目质量得分排名,该得分是根据从GitHub和不同程序包管理器自动收集的各种指标计算得出的。目录资源列表......
  • 如何使用 40 个 ChatGPT 插件包括搜索网络
    OpenAI提供了超过540个ChatGPT插件,其中近20%搜索网络。了解这些AI驱动的搜索工具的工作原理以及哪些工具最好。随着最近ChatGPT插件商店的扩展,不断发展的人工智能格局又向前迈进了一大步。该商店以提供大量增强ChatGPT功能的插件而闻名,现在拥有近550个插件的令人印象深刻的......
  • NC24048 [USACO 2017 Jan P]Promotion Counting
    题目链接题目题目描述Thecowshaveonceagaintriedtoformastartupcompany,failingtorememberfrompastexperiencethatcowsmaketerriblemanagers!Thecows,convenientlynumbered1…N(\(1\leqN\leq100,000\)),organizethecompanyasatree,withco......
  • 后缀数组
    ......
  • 代码随想录算法训练营第十五天| 110.平衡二叉树 (优先掌握递归) 257. 二叉树的所有路径
     110.平衡二叉树(优先掌握递归)难点:要求每个节点的左右字数的高度相减<=1,因此,需要对每个节点都进行检查,难就难在怎么获得任意节点的高度其中递归是最简单的: 1intisB_cursor(TreeNode*node,bool&isBalance)2{3if(isBalance==false)return0;4if......
  • GPT-4零失误通关大厂模拟面试,offer拿到手软?与AGI首次接触
    “GPT-4可被视作AGI(通用人工智能)的早期版本。”如若从他人口中说出,或许是无稽之谈——但是由微软雷蒙德研究院机器学习理论组负责人万引大神SébastienBubeck与2023新视野数学奖得主RonenEldan、2023新晋斯隆研究奖得主李远志、2020斯隆研究奖得主YinTatLee等科学家共同撰写的......
  • 后台报401,怎么处理
    1、401状态码的含义axios向服务器端发送请求时,服务器端有些api接口要求传递token,token失效或没有传递,就会报401错误服务端要求传递token信息,而实际发送请求时没有传递。发送请求时有传递token到达服务器端,但由于时间比较久,这个token在服务器中已经过期了(服务器存储token有效......