leetcode 算法解析(一):260. Single Number III

260.Single Number II 原题连接java

  • 本题其实算是比较简单,在 leetcode 上也只是 medium 级别,ac 率也很高,最好先本身尝试,本文只是单纯的记录一下本身总体的思路;算法

  • 在阅读本文章以前,最好先解锁本题的简单模式 136.Single Number,这对理解本题有较大的帮助;数组

  • 还有不少细节做者都没有写进去,是为了留给读者一点思考的空间,实际上是由于懒spa

  • 因为做者我的水平等缘由,出现错误在所不免,还望各位看官海涵。3d

注意 Note 中的第一个条件:The order of the result is not important.,这个条件很是重要,这关系到算法的正确性。code


而后给出整个算法的具体思路,假设数组中两个不一样的数字为 A 和 B;blog

  1. 经过遍历整个数组并求整个数组全部数字之间的 XOR,根据 XOR 的特性能够获得最终的结果为 AXORB = A XOR B图片

  2. 经过某种特定的方式,咱们能够经过 AXORB 获得在数字 A 和数字 B 的二进制下某一位不相同的位;由于A 和 B 是不相同的,因此他们的二进制数字有且至少有一位是不相同的。咱们将这一位设置为 1,并将全部的其余位设置为 0,咱们假设咱们获得的这个数字为 bitFlag;ip

  3. 那么如今,咱们很容易知道,数字 A 和 数字 B 中必然有一个数字与上 bitFlag 为 0;由于bitFlag 标志了数字 A 和数字 B 中的某一位不一样,那么在数字 A 和 B 中的这一位必然是一个为 0,另外一个为 1;而咱们在 bitFlag 中将其余位都设置为 0,那么该位为 0 的数字与上 bitFlag 就等于 0,而该位为 1 的数字与上 bitFlag 就等于 bitFlagleetcode

  4. 如今问题就简单了,咱们只须要在循环一次数组,将与上 bitFlag 为 0 的数字进行 XOR 运算,与上 bitFlag 不为 0 的数组进行独立的 XOR 运算。那么最后咱们获得的这两个数字就是 A 和 B。

先给出具体实现,引用自 proron's Java bit manipulation solution,我修改了部分代码以便于理解:

public class Solution {
    public int[] singleNumber(int[] nums) {
        int AXORB = 0;
        for (int num : nums) {
            AXORB ^= num; 
        }
        // pick one bit as flag
        int bitFlag = (AXORB & (~ (AXORB - 1)));
        int[] res = new int[2];
        for (int num : nums) {
            if ((num & bitFlag) == 0) {
                res[0] ^= num;
            } else {
                res[1] ^= num;
            }
        }
        return res;
    }
}

接下来,咱们一行行的解析代码:

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

这段代码在 136.Single Number 已经解析过,很容易理解最后获得的结果:假设数组中不一样的数字为 A 和 B,那么 最后获得的结果是 A XOR B。

随后的这一行代码是整个算法中的难点:

//pick one bit as flag
int bitFlag = (AXORB & (~ (AXORB - 1)));

这一行代码的做用是:找到数字 A 和数字 B 中不相同的一位,并将该位设置为 1,其余位设置为 0;
根据 XOR 的定义,咱们知道,在 AXORB 中,为 1 的位即 A 和 B 不相同的位,AXORB 中为 0 的位即 A 和 B 中相同的位
因此,要找到 A 和 B 中不相同的位,只须要找到在 AXORB 中从右往左第一个为 1 的位,保留该位并将其余位置为 0 便可。

//其实这一行与下面的代码等价,可是论逼格就差远了(手动斜眼
public static int f(int num){
    int times = 0;
    while(num > 0){
        if(num % 2 == 1){
            break;
        }
        times++;
        num = num >> 1;
    }
    
    return 1 << times;
}
//下面这个返回 true
System.out.println(Stream.iterate(1, num -> num + 1).limit(Integer.MAX_VALUE).allMatch(num -> f(num)==(num & (~(num -1)))));

咱们能够把这一行代码解析为三步:

int tmp0 = AXORB - 1;
  • 假设 AXORB 从右往左出现的第一位非 0 数字出如今第k位,那么数字 AXORB 能够表示为,可能等于 0:

图片描述

若是 a0 = 1;那么问题很是简单, AXORB - 1 能够表示为:

图片描述

int tmp1 = ~tmp0;

图片描述

int bitFlag = AXORB & tmp1;

图片描述

由前面假设咱们知道 a0=1,因此很明显, bitFlag = 1;

若是 a0 != 1,咱们一样很容易得出这一行代码的做用就是将数字 AXORB 的从右到左第一个出现 1 的位置为 1,其余位所有置为 0。其实一点都不容易,只不过用 laTex 写数学公式好蛋疼啊,因此偷下懒

到这里,整个算法基本上就没什么难点了,你们自行理解吧。

我了个大槽,我原本只是想试试新学的 laTex,结果他喵的就写了这么多。大写加粗的坑

相关文章
相关标签/搜索