• 剑指Offer——数组中只出现一次的数字(一个很帅的异或解法)


    题目:

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

    看题目脑子里就出现做法了:

    遍历,用个HashMap来记录出现的次数,然后再遍历HashMap返回的EntrySet找出出现一次的数字搞定。

     代码顺便上下吧:

    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
            Integer count;
            for(int temp : array) {
                if( (count = map.get(temp)) == null )map.put(temp, 1);
                else map.put(temp, ++count);
            }
            num1[0] = -1;num2[0] = -1;
            Set<Entry<Integer, Integer>> entrySet = map.entrySet();
            for(Entry<Integer, Integer> temp : entrySet) {
                if( (count = temp.getValue()) == 1 && num1[0] == -1)num1[0] = temp.getKey();
                else if(count == 1)num2[0] = temp.getKey();
            }
        }

    然后看牛客网的讨论,原来可以用异或操作很帅气地解决这个问题哈哈,来记录一下。

    基础知识:

    1. 0和任何数字异或都为它自己,这个也很容易理解,拿个数字试试就知道辽。
    2. 结合律:a ⊕b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c;
    3. a ⊕ b ⊕ a = b.
    当然还有其他运算规律,可以去百度百科看哈哈:

    思路:(来自牛客的一个小伙子)

    链接:https://www.nowcoder.com/questionTerminal/e02fdb54d7524710a7d664d082bb7811
    来源:牛客网
    只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。
     
    依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果这个结果的二进制中的1,表现的是A和B的不同的位
     
    我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。

    然后上一下我自己根据这个思路写的代码,已经通过:

    /*高端的位运算法*/
        public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            int firstXorResult = 0;//0和任何数异或都为本身
            for(int temp : array) {
                firstXorResult ^= temp;//异或满足结合率,a^b^a = b
            }
            //这样得到的firstXorResult就是唯一两个只出现一次的数字的亦或结果,也就是这个firstXorResult中为1的地方,就是这两个数字在位上面不同的地方
            int shiftBit = 0;
            while(shiftBit <= 31) {//这个循环是看从最低位开始,哪个位是第一个不同的,也就是为1的
                if(  (firstXorResult & (1 << shiftBit)) != 0 )break;
                shiftBit++;
            }
            List<Integer> list1 = new ArrayList<Integer>();
            List<Integer> list2 = new ArrayList<Integer>();
            
            for(int temp : array) {
                //根据刚刚的shiftBit,把数字分成两份,一份相与为0,一份不为0。首先肯定那两个只出现一次的数字肯定分开的;然后相同的数字肯定分到一个list
                if(  (temp & (1 << shiftBit)) == 0  ) {
                    list1.add(temp);
                } else {
                    list2.add(temp);
                }
            }
            
            int result1 = 0, result2 = 0;
            for(int temp : list1) {//异或结束得到的就是结果
                result1 ^= temp;
            }
            for(int temp : list2) {
                result2 ^= temp;
            }
            num1[0] = result1;
            num2[0] = result2;
        }
  • 相关阅读:
    TypeError: Buffer.alloc is not a function
    node.js服务端程序在Linux上持久运行
    C#中的反射
    群要事日记
    vs2017 自定义生成规则 错误 MSB3721 命令 ”已退出,返回代码为 1。
    VP9 Video Codec
    用户手册是Yasm汇编
    更改Mysql数据库存储位置
    注册表项
    C#开发可以可视化操作的windows服务
  • 原文地址:https://www.cnblogs.com/wangshen31/p/10663819.html
Copyright © 2020-2023  润新知