zoukankan      html  css  js  c++  java
  • 每日一题

    题目信息

    • 时间: 2019-07-05

    • 题目链接:Leetcode

    • tag:位运算

    • 难易程度:中等

    • 题目描述:

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

    示例1:

    输入:nums = [4,1,4,6]
    输出:[1,6] 或 [6,1]
    

    示例2:

    输入:nums = [1,2,10,4,1,4,3,3]
    输出:[2,10] 或 [10,2]
    

    注意

    1. 2 <= nums.length <= 10000
    

    解题思路

    本题难点

    查找数组中两个只出现一次的数字,性能最优。

    具体思路

    异或运算

    • 交换律:p⊕q=q⊕p
    • 结合律:p⊕(q⊕r)=(p⊕q)⊕r
    • 恒等率:p⊕0=p
    • 归零率:p⊕p=0

    如果有若干个数字进行异或操作:根据 交换律、结合律 将相同的数字优先两两进行异或运算。此时就只剩下只出现一次的那个数了!

    由于数组中存在着两个数字不重复的情况,我们将所有的数字异或操作起来,最终得到的结果是这两个数字的异或结果:(相同的两个数字相互异或,值为0)) 最后结果一定不为0,因为有两个数字不重复。

    此时的难点在于,对两个不同数字的分组。

    通过 & 运算来判断一位数字不同即可分为两组,那么我们随便两个不同的数字二进制上至少也有一位不同吧!
    我们只需要找出那位二进制上不同的数字mask,即可完成分组( & mask )操作。

    不同数字mask查找演示:

    num1:       101110      110     1111
    num2:       111110      001     1001
    num1^num2:  010000      111     0110
    
    可行的mask:  010000      001     0010
                            010     0100
                            100     
    

    所有的可行 mask 个数,都与异或后1的位数有关。

    提示:为了操作方便,我们只去找最低位的mask:

    代码

    lass Solution {
        public int[] singleNumbers(int[] nums) {
           //用于将所有的数异或起来
            int k = 0;
            for(int num : nums){
                k ^= num;
            }
          //获得k中最低位的1
            int mask = 1;
            while((mask & k) == 0){
                mask <<= 1;
            }
    
            int a = 0;
            int b = 0;
          //将数组分为包含不同数字的两组
            for(int num : nums){
                if((num & mask) == 0){
                    a ^= num;
                }else{
                    b ^= num;
                }
            }
            return new int[]{a,b};
        }
    }
    

    复杂度分析:

    • 时间复杂度 O(N) :只需要遍历数组两次。
    • 空间复杂度 O(1) : 只需要常数的空间存放若干变量。

    其他优秀解答

    解题思路

    通过二分查找,将包含两不同数字的数组分组。

    异或和为 0其实并不代表 要找的元素不在这里面,因为有可能 0只出现了 1次! 所以这种思路需要特判一下某个数为 0的情况。

    代码

    class Solution {
        public int[] singleNumbers(int[] nums) {
            int sum = 0, min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, zeroCount = 0;
            for (int num: nums) {
                if (num == 0) {
                    zeroCount += 1;
                }
                min = Math.min(min, num);
                max = Math.max(max, num);
                sum ^= num;
            }
            // 需要特判一下某个数是0的情况。
            if (zeroCount == 1) {
                return new int[]{sum, 0};
            }
            int lo = min, hi = max;
            while (lo <= hi) {
                // 根据 lo 的正负性来判断二分位置怎么写,防止越界。
                int mid = (lo < 0 && hi > 0)? (lo + hi) >> 1: lo + (hi - lo) / 2;
                int loSum = 0, hiSum = 0;
                for (int num: nums) {
                    if (num <= mid) {
                        loSum ^= num;
                    } else {
                        hiSum ^= num;
                    }
                }
                if (loSum != 0 && hiSum != 0) {
                    // 两个都不为0,说明 p 和 q 分别落到2个数组里了。
                    return new int[] {loSum, hiSum};
                }
                if (loSum == 0) {
                    // 说明 p 和 q 都比 mid 大,所以比 mid 小的数的异或和变为0了。
                    lo = mid + 1;
                } else {
                    // 说明 p 和 q 都不超过 mid
                    hi = mid - 1;
                }
            }
            // 其实如果输入是符合要求的,程序不会执行到这里,为了防止compile error加一下
            return null;
        }
    }
    
  • 相关阅读:
    【C++】资源管理
    【Shell脚本】逐行处理文本文件
    【算法题】rand5()产生rand7()
    【Shell脚本】字符串处理
    Apple iOS产品硬件参数. 不及格的程序员
    与iPhone的差距! 不及格的程序员
    iPhone游戏 Mr.Karoshi"过劳死"通关. 不及格的程序员
    XCode V4 发布了, 苹果的却是个变态. 不及格的程序员
    何时readonly 字段不是 readonly 的?结果出呼你想象!!! 不及格的程序员
    object file format unrecognized, invalid, or unsuitable Command 不及格的程序员
  • 原文地址:https://www.cnblogs.com/ID-Wangqiang/p/13245655.html
Copyright © 2011-2022 走看看