zoukankan      html  css  js  c++  java
  • 剑指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;
        }
  • 相关阅读:
    TCP源码—连接建立
    TCP系列02—连接管理—1、三次握手与四次挥手
    TCP系列01—概述及协议头格式
    ubuntu软件管理apt与dpkg
    318. Maximum Product of Word Lengths
    317. Shortest Distance from All Buildings
    316. Remove Duplicate Letters
    315. Count of Smaller Numbers After Self
    314. Binary Tree Vertical Order Traversal
    313. Super Ugly Number
  • 原文地址:https://www.cnblogs.com/wangshen31/p/10663819.html
Copyright © 2011-2022 走看看