• 剑指40.数组中只出现一次的数字


    题目描述

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

    思路

        根据题意,如果要求空间复杂度为O(1),则不能使用哈希表了。这道题考察的是位运算。

    Tips:如果数组中只有一个数字只出现一次,从头到尾 异或 每个数字,那么最终的结果刚好是那个只出现一次的数字。原因:异或运算满足交换律,那些成对出现两次的数字全部在异或中抵消了。 

        那么对于本题来说,数组中有两个数字只出现一次,如果能够将数组分为两部分,两部分中都只有一个数字只出现一次,那么就可以解决该问题了。此时,我们从头到尾异或每个数字,最终得到的结果是两个只出现一次的数字的异或结果。由于这两个数字不同,那么在这个结果数字的二进制表示中至少有1位是1。我们把结果中从右往左第一个为1的位置记为第 n 位,因为这是两个只出现一次的数字的异或结果,所以这两个数字在第 n 位上的数字一定是1和0.

        现在我们以第 n 位是不是1为标准把原数组中的数字分成两部分,第一个子数组中每个数字第 n 位都是1,而第二个子数组每个数字第 n 位都是0. 因为两个相同的数字的任意一位都是相同的,因此出现了两次的数字一定会被分配到同一个子数组。

        最后,对两个子数组从头到尾异或,就可以得到这两个数了。

    解法

    //num1,num2分别为长度为1的数组。传出参数
    //将num1[0],num2[0]设置为返回结果
    import java.util.*;
    public class Solution {
        // 使用HashMap
        /*public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            if (array == null || array.length < 2)
                return;
            HashMap<Integer,Integer> map = new HashMap<>();
            for (int num : array){
                map.put(num,map.getOrDefault(num,0) + 1);
            }
            boolean flag = true;
            for (int key : map.keySet()){
                if (map.get(key) == 1){
                    if (flag){
                        num1[0] = key;
                        flag = false;
                    }else{
                        num2[0] = key;
                    }
                }
            }
        }*/
        public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            if (array == null || array.length < 2)
                return;
            int ans = 0;
            // 第一个循环找到只出现一次的那两个数字的异或结果
            for (int num : array)
                ans ^= num;
            // 从右往左找到第一个不为0的位置对应的数,用来分割数组。
            int indexOf1 = 1;
            while ((ans & indexOf1) == 0)
                indexOf1 <<= 1;
            num1[0] = 0;
            num2[0] = 0;
            for (int x : array){
                if ((x & indexOf1) == 0){
                    num1[0] ^= x;
                }else{
                    num2[0] ^= x;
                }
            }
        }
    }

    收获

    当一个数字出现两次(或者偶数次)时,用异或^ 可以进行消除。

    参考:

    【Java】 剑指offer(56-1) 数组中只出现一次的两个数字
  • 相关阅读:
    [LeetCode] 5. 最长回文子串
    [LeetCode] 572. 另一个树的子树
    [LeetCode] 983. 最低票价
    [LeetCode] 98. 验证二叉搜索树
    [LeetCode] 3. 无重复字符的最长子串
    [LeetCode] 21. 合并两个有序链表
    [LeetCode] 202. 快乐数
    [LeetCode] 面试题 01.07. 旋转矩阵
    [LeetCode] 面试题56
    个人网站实现支持https
  • 原文地址:https://www.cnblogs.com/HuangYJ/p/13558008.html
Copyright © 2020-2023  润新知