LeetCode专题------位操作

来源:互联网 发布:织田裕二 知乎 编辑:程序博客网 时间:2024/05/15 16:03

1、LeetCode190. Reverse Bits
Reverse bits of a given 32 bits unsigned integer.

For example, given input 43261596 (represented in binary as 00000010100101000001111010011100), return 964176192 (represented in binary as 00111001011110000010100101000000).

Follow up:
If this function is called many times, how would you optimize it?

Related problem: Reverse Integer

分析:需要借助另外一个整数m,这样相当于有两个桶,里面装的是01比特。首先把n的最低位取出,放到m的最低位,而m不断左移把低位移到自己的高位,而n把取出的最低位通过右移移出。最终,m就是需要的结果。其中的关键是如何取出n的最低位放到m的最低位?其实就是两个位操作,先让n和1进行与,这样相当于把n的最低位单独取出,然后让m和刚才与的结果进行或,由于m的最低位始终保持为0,任何数和0或都是其本身,所以这一步就相当于把n的最低位放到了m的最低位。

public class Solution {    // you need treat n as an unsigned value    public int reverseBits(int n) {        int m = 0;        for(int i=0;i<32;i++){            m<<=1; //左移并赋值            m = m|(n & 1); //若n的最后一位为0,m不变,若为1,则m的最后一位变为1            n>>=1; //右移并赋值        }        return m;    }}

2、leetcode191. Number of 1 Bits
Write a function that takes an unsigned integer and returns the number of ’1’ bits it has (also known as the Hamming weight).

For example, the 32-bit integer ’11’ has binary representation 00000000000000000000000000001011, so the function should return 3.

分析:关键就是n&(n-1)这一句,这句话相当于是清除了n最低位的1。

public class Solution {    // you need to treat n as an unsigned value    public int hammingWeight(int n) {        int counter = 0;        while(n != 0) {            n = n & (n-1); //只要n不为0,n&(n-1)肯定比n的hamming距少1            counter ++;        }        return counter;    }}

3、判断一个整数是否是2的次方?是否是4的次方?

public class Solution {    public boolean isPowerOfTwo(int n) {             return (n>0) && ((n&(n-1))==0) ? true:false;    }}
public class Solution {    public boolean isPowerOfFour(int num) {        return (num>0) && ((num&(num-1))==0) && ((num&0x55555555)!=0) ? true : false;    }}

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?

分析:这题的关键就在于线性时间内把相同的一对 “消掉”,留下那个 “落单” 的。

public class Solution {    public int singleNumber(int[] nums) {        for(int i=1;i<nums.length;i++) {            nums[i] ^= nums[i-1]; //异或的性质,交换律 结合律 两个相同的数异或为0 一个数和0异或是其本身        }        return nums[nums.length-1];    }}

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?
分析:上一道题是重复两次,异或可以达到我们的需求,但是对于三次异或不行,思路是类似的,只不多需要我们自己来实现类似异或可以去除三次重复的功能。对于一个32位(或者64位)的整数,对于这32个位中的某个位而言,如果每个数都出现三次,那么对于所有数在这个位上“1”的个数,一定是 3 的倍数;而反之,如果存在某个数不是出现 3 次(也不是 3 的倍数次,这是题目未讲明处,I 亦同理如此),那么对于它的二进制展开后数为 1 的位而言,对于所有数在这个位上“1”的个数,一定不是 3 的倍数。

public class Solution {    public int singleNumber(int[] nums) {        if(nums==null || nums.length==0)            return -1;        int rst = 0; //存储最终结果        for(int i=0;i<32;i++) {            int bitCount = 0; //统计某一位出现的次数            for(int j=0;j<nums.length;j++) {                if(((nums[j]>>i)&1)==1) //这个是判断nums[j]的第i位是否为1                    bitCount++;            }            bitCount %= 3; //这个结果就是那个只出现一次的数的第i位            rst |= (bitCount<<i); //把这一位放到rst的第i位        }        return rst;    }}

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?

分析:假设我们要找的这两个数为 a, b, 而 x = a ^ b。

首先,a 肯定不等于 b,那么说明它们的二进制位一定是不完全相同的,所以 x 肯定不为 0。也就是说,a 与 b 一定存在“某一位”,使得在它们中的某个数中是 0,而在另一个数中是 1,这是他们之间的一个差别。我们可不可以利用这个差别来把这两个数从 x 中揪出来呢?
是可以的。
利用这个差别,我们可以将整个 nums 集合分成两个集合。一个集合中是这 “某一位” 为 0 的在nums中的所有数,假设为集合 A。而另一个集合是这 “某一位” 为 1 的在nums中的所有数。假设 a 的这 “某一位” 是 0 ,b 的 这个“某一位”是1,那么显然 a 在集合 A 中,b 在集合 B 中,这样问题就完全转化成了与 I 一样的两个子问题,于是可以得解。
关于具体的代码实现,还有一点说明:
我们如何找到这个 “某一位” 呢?理论上,只要是在这一位上 a与b的值不同,都可以合格的成为我们需要找的某一位。既然无更多限制,那我们肯定是找好找的某一位咯。
我们可以用很常规和易懂的方法去找,但一般而言,我们肯定是找最右边(低位)那边符合要求的“某一位”嘛。更进一步说,就是找到 x 中最低位的 1 嘛。那当然我们可以从最低位开始枚举每一位,直到找到我们需要找的那个“某一位”。
还有一种更trick利用位运算的办法:找到 x 中最低位的 1。
当我们统计某个数的二进制展开中1的个数的时候,我们使用了一个技巧,即用 n &= n - 1 每次来清除 n 中当前最右边的那个 1。
n-1 是把 n 的最低位的 1 变成 0,并且使更低位的 0 全部变成 1,然后异或一下就把 最低位的 1 及其更低位全部都变成了 0,即达到了“清除最低位的 1 的目的”。
在这个地方,我们需要逆向思维,即 保留 最低位的1,并且最好使得其他位 都变成0,这样我直接与 nums 中的每一个数相与,就可以直接将它们分成 A 和 B 两个集合了。
逆向思维会是这样:
n-1 的过程其实是把 最低位 1 和 更低位 都位反转 (Bit Flipping) 的过程,那我们这里 首先也将 n 的所有位反转得到 n’。
然后我们再把 n’+1。。。
Opps! What did we get?
我们发现 n’+1 相对于 n 而言,最低位的1及其更低位的0 都没变,而其他位(比 最低位1 更高的位)都反转了。
那此时如果用 n & (n’+1) 得到的便是 除 n 中最低位 1 继续为 1 以外,其他各位都为 0 的数了。
n’ 如何求?当然我们可以直接取反。但是联合 n’+1 来看,各个位取反再加一,这不正是计算机中 “负数补码” 的表示方式!
所以我们可以直接用 n &= -n 得到 “除 n 中最低位 1 继续为 1 以外,其他各位都为 0 的数”!
(注意正数的补码的补码是它本身,所以即便 n 本身是负数,-n是正数,但 -n 依然是求 n 的补码。)

public class Solution {    public int[] singleNumber(int[] nums) {        int diff = 0;        for(int i=0;i<nums.length;i++) {            diff ^= nums[i]; //这样得出来的结果相当于两个只出现一次的数异或        }        diff &= -diff; //这样就把diff中最低位的1取出了,这个1表示了两个只出现一次的数在这个位置上的二进制位是不同的        int[] rst = new int[2]; //用来存放最终的结果        for(int i=0;i<nums.length;i++) {            //下面先将nums中的数划分为两类,两个只出现一次的数分别分到了两类中,然后再按照第一道题的方法找出            if((nums[i]&diff)==0)                rst[0] ^= nums[i];            else                 rst[1] ^= nums[i];        }        return rst;    }}

待更新。。。

0 0
原创粉丝点击