[leetcode]Single Number

来源:互联网 发布:枫树浏览器 知乎 编辑:程序博客网 时间:2024/06/02 15:30

刷微博时看见的一道网易面试题,竟然一点印象都没有,后来才知道是leetcode上面的原题,而且是一个系列的都是关于位运算的。

136. Single Number

Given an array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
Subscribe to see which companies asked this question

因为是出现两次,所以第一反映肯定是异或操作。

    public int singleNumber(int[] nums) {        int re=0;        for(int num: nums){            re^=num;        }        return re;    }

137. Single Number II

Given an array of integers, every element appears three times except for one. Find that single one.

Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

这次是出现三次了,奇数次,肯定不能直接异或。还是位运算的思路,计算每个位上1的个数,如果每个数字都出现3次,那么最后肯定都是3的倍数。所以每位上1的个数对3取余后剩下的,就是单独出现的那个数。

算法一:按位保存,一次遍历int累i系那个的32位,累计每位出现的1的个数,超过3以后就清零。

   public static int singleNumber2(int[] nums) {        if(nums.length==1){            return nums[0];        }        int re=0;        for(int i=0; i<32; i++){            int temp=0;            for(int num: nums){                temp+=(num>>i)&1;            }            temp=temp%3;            re|=(temp<<i);        }        return re;    }

算法二:利用三个变量分别保存出现一次,两次,三次的位。最后返回出现一次的,就是要求的值。

        public int singleNumber4(int[] nums) {        int one=0;        int two=0;        for(int i=0; i<nums.length; i++){            two|=nums[i]&one;//先计算出现两次的            one^=nums[i];//再更新出现一次的            int three=one&two;//出现三次            one&=~three;//出现三次就清零            two&=~three;//出现三次就清零        }        return one;    }

260. Single Number III

Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.

For example:

Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].

Note:
The order of the result is not important. So in the above example, [5, 3] is also correct.
Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?

虽然还是出现两次,但是这次有两个数了。同样的,先异或一遍,但是,这样得到的值是两个数异或的结果,那么问题就在于怎么利用这个异或的结果来将两个数分开。

    public static int[] singleNumber3(int[] nums) {        int[] re=new int[2];        int bit=0;        for(int num: nums){            bit^=num;        }        bit=bit&(-bit);        for(int num:nums){            if((bit&num)==0){                re[0]^=num;            }else{                re[1]^=num;            }        }        return re;          }

这里用到了一个位运算中常用的技巧,就是x&(-x),即取右边的一个的1也是唯一的一个1,可能表达的不是很清楚,举个例子。

int x=11;那么x的后八位二进制表达就是0000 1011,
那么-x的二进制是多少呢,就是下单反码即~x+1,也就是1111 0101,
所以,x&(-x)的结果就是0000 0001。

这样,通过x&(-x)操作,就可以得到一个只有一个位为1的数,且这个为1的位是x右边第一个为1的位。

回到这道题里面,第一次循环异或操作得到了两个数异或的结果bit,然后通过对bit进行bit&(-bit)操作,得到了一个只有一位为1的数。而这个为1的位在原来的bit中也是1,所以可以得知在答案中两个数的这个位上,肯定是不同的,所以异或的结果才会是1。利用这个特点,再将新的bit异或一遍数组,那么就可以通过num&bit,来区别这两个数,从而得到最后的答案。

总结

  • leetcode的上面的题刷了一遍之后再看竟然没什么印象了,说明记的还是不牢固,所以再博客上记录下来希望能加深记忆。
  • 关于位运算的几个常用的操作一定要记熟,尤其是x&(-x)和x&(x-1)用的特别多。还有就是异或操作的巧妙运用。
  • 第一次用markdown编辑器,好用到爆。再也不用担心排版问题了。
0 0
原创粉丝点击