面试题56 - I. 数组中数字出现的次数

面试题56 - I. 数组中数字出现的次数

https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/

算法思想

位运算,异或运算

思路

  • 由于异或运算之后,相同的两个数都变成0这个特性。
  • 采用两次遍历异或运算的方式。第一步异或,相同的数其实都抵消了,剩下两个不同的数。这两个数异或结果肯定有某一位为1,不然都是0的话就是相同数。找到这个位,不同的两个数一个在此位为0,另一个为1。按此位将所有数分成两组,分开后各自异或,相同的两个数异或肯定为0(而且分开的时候,两个数必为一组)。剩下的每组里就是要找的数。

代码实现

public class SingleNumbers {
    public int[] singleNumbers(int[] nums) {

        int sum = 0;
        for (int num : nums) {
            sum ^= num;
        }

        int firstO = 1;
        while ((sum & firstO) == 0) {
            firstO <<= 1;
        }
        int a = 0;
        int b = 0;

        for (int num : nums) {
            if ((num & firstO) == 0) {
                a ^= num;
            } else {
                b ^= num;
            }
        }

        return new int[]{a,b};
    }
}

复杂度

时间复杂度是O(N): 两次便利nums数组。
空间复杂度O(1):只使用了常数个临时变量。

位运算补充知识

  1. 左移或者右移运算(计算m*2^n)
  • 左移是乘以2的幂,对应着右移则是除以2的幂
  • 任何数左移(右移)32的倍数位等于该数本身。
  • 在位移运算m<<n的计算中,若n为正数,则实际移动的位数为n%32,若n为负数,则实际移动的位数为(32+n%32),右移,同理。
  1. 判断一个数的奇偶性
    n&1 == 1?”奇数”:”偶数”,若n的二进制最低位是1(奇数)与上1,结果为1,反则结果为0
  2. 不用临时变量交换两个数
nums[i]= nums[i]^nums[j];
nums[j] = nums[j]^nums[i];
nums[i] = nums[i]^nums[j];

异或的常用公式:b^(a^b)=a,a ^b ^ c = a ^ (b ^ c) = (a ^ b) ^ c; d = a ^b ^ c 可以推出 a = d ^ b ^ c.,a ^ b ^a = b.
4. 取绝对值
先整理一下使用位运算取绝对值的思路:若a为正数,则不变,需要用异或0保持的特点;若a为负数,则其补码为源码翻转每一位后+1,先求其源码,补码-1后再翻转每一位,此时需要使用异或1具有翻转的特点。
任何正数右移31后只剩符号位0,最终结果为0,任何负数右移31后也只剩符号位1,溢出的31位截断,空出的31位补符号位1,最终结果为-1.右移31操作可以取得任何整数的符号位。

(a^(a>>31))-(a>>31)
那么综合上面的步骤,可得到公式。a>>31取得a的符号,
若a为正数,a>>31等于0,a^0=a,不变;
若a为负数,a>>31等于-1 ,a^-1翻转每一位.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值