[leetcode] Summary of Bit Manipulation

今天刷了几道题,发现leetcode现在都有分类了,所以顺便把以前刷过的bit manipulation相关的题目都看了一遍,下面简单总结一下。

1. Single Number

题目大意:在给定的整数数组中,除了一个数字之外,其他数字都出现了两次,现让求出只出现一次的那个数字。

基本思想:遍历数组一次,将所有数字依次进行异或运算,结果即为所求的数字。因为其他数字都恰好出现了两次,异或得0。


2. Single Number II

题目大意:还是一个整数数组,但是这个数组中除了一个元素其他元素都出现了三次,现让求出只出现一次的那个数字。

基本思想:还是遍历一遍数组,但不止一次,而是32次(32位),每次看数组中所有元素在相应的位上的数字是0还是1,统计1的数目。若该位上1的数字整除3,则表示所求数字在该位为0,否则该位为1。(这种方法可以适用其他元素出现4,5,6...等次的情况)

int singleNumber(int A[], int n) {
        int count = 0;
        int result = 0;
        for (int i = 0; i < 32; i++) {
            count = 0;
            for (int j = 0; j < n; j++) {
                count += ((A[j] >> i) & 1);
            }
            result |= ((count % 3) << i);
        }
        return result;
    }


3. Single Number III

题目大意:还是一个整数数组,但是这个数组中除了两个元素之外,其他元素都出现且仅出现两次,现在求只出现一次的两个数字。(为方便叙述,这里不妨设这两个数字为A和B)

基本思想:第一遍遍历数组,按照1中的方法可以求出C=A^B。我们可以利用C ^= (C & (C-1))将C变成了仅保留最低位的1的数字,这样C就可以用来作为区分A和B的标志。因为C是A和B按位异或的结果,C上的1即表示这位上A和B是不同的。因此第二遍遍历数组,我们将所有数字按照&C的结果分成两类,这样这两类分别异或后的结果即对应A和B。


vector<int> singleNumber(vector<int>& nums) {
        vector<int> ret(2, 0);
        int n = nums.size();
        int aXORb = 0;
        for(int i = 0; i < n; ++i)
        {
            aXORb ^= nums[i];
        }
        aXORb ^= (aXORb & (aXORb - 1));
        for(int i = 0; i < n; ++i)
        {
            if(nums[i] & aXORb) ret[0] ^= nums[i];
            else ret[1] ^= nums[i];
        }
        return ret;
    }

4. Reverse Bits

题目大意:将32位的整数按位反转,输入和输出都是uint32_t。

基本思路:考验两种比较基础的位操作:取出一个整数某一位上的值,将一个整数的某一位上赋值(0或1)。有了这两个基本操作之后,bit的反正和字符串的反转就类似了。

uint32_t reverseBits(uint32_t n) {
        uint32_t ret = 0;
        for(int i = 0; i < 32; ++i)
        {
            if(n & 1) ret |= (1 << (31-i));
            n >>= 1;
            if(0 == n) break;
        }
        return ret;
    }

5. Repeated DNA Sequences

参见 前几天的一篇博客 [leetcode] Repeated DNA Sequences

vector<string> findRepeatedDnaSequences(string s) {
        vector<string> ret;
        if(s.length() < 10) return ret;
        
        unordered_map<char, int> mapNucleotide;
        mapNucleotide['A'] = 0;
        mapNucleotide['T'] = 1;
        mapNucleotide['C'] = 2;
        mapNucleotide['G'] = 3;
        
        unordered_map<int, int> mapStrCnt;
        int base = ~(3 << 18);
        int bits = 0;
        for(int i = 0; i < 10; ++i)
        {
            bits <<= 2;
            bits += mapNucleotide[s[i]];
        }
        mapStrCnt.insert(pair<int, int>(bits, 1));
        
        for(int i = 10; i < s.length(); ++i)
        {
            bits &= base;
            bits <<= 2;
            bits += mapNucleotide[s[i]];
            if(mapStrCnt.find(bits) != mapStrCnt.end())
            {
                ++mapStrCnt[bits];
                if(2 == mapStrCnt[bits]) ret.push_back(s.substr(i-9, 10));
            } else {
                mapStrCnt.insert(pair<int, int>(bits, 1));
            }
        }
        return ret;
    }


6. Number of 1 Bits

题目大意:求一个无符号整型的二进制表示中有多少个1。

主要思路:最暴力的办法就是每一位遍历一遍看是否为1,求出总数。然而位运算能够为我们提供一个O(k)的算法,这里k即为1的个数。这个位运算就是n & (n-1),因为n和n-1的二进制表示中肯定高位部分是相同的,低位部分是相反的,两者进行与运算后恰好可以把n的二进制表示中最低位的1消除。

int hammingWeight(uint32_t n) {
        int ret = 0;
        while(n > 0)
        {
            ++ret;
            n &= (n-1);
        }
        return ret;
    }


7. Power of Two

题目大意:判断一个整数是否为2的幂。

基本思路:搞清楚2的幂的特征即可,就是二进制表示中只有一位是1,其他位全是0。当n的二进制表示只有一个1的时候,n & (n-1)就等于0。不过这题需要注意的是非负数都要排除,2^0 = 1,所以2的幂最小是1,我竟然有一瞬间认为0和-4都是2的幂,没文化好可怕!!

bool isPowerOfTwo(int n) {
        if(0 >= n) return false;
        else return ((n & (n-1)) == 0);
    }

8. Missing Number

题目大意:A为含有n个元素的整数数组,其中这n个元素各不相同,且取值范围为[0, n],因此[0, n]中肯定存在一个数字没有出现在A中,求出这个数字。

基本思路:A中所有元素进行异或运算得到XOR1,1到n进行异或运算得到XOR2(这里之所以用到1..n的所有数字异或是考虑到奇偶个数字的影响),所求的数字即为XOR1与XOR2的异或。当然还有一种更直观的方法是求和公式n(n+1)/2-sum(A)。

int missingNumber(vector<int>& nums) {
        int n = nums.size();
        if(n == 0) return -1;
        int ret = 0;
        for(int i = 0; i < n; ++i)
        {
            ret ^= nums[i] ^ (i+1);
        }
        return ret;
    }

9. Bitwise AND of Numbers Range

题目大意:给定两个非负整数m, n (m <= n),求出m到n所有数字(包含m和n)按位与的结果。

基本思路:注意按位与运算的特性,0与任何元素都是0,所以某一位上只有这些数字中有出现0,则最终结果上该位就是0。那么我们再来观察m和n,当m==n时,结果为m本身;当n更大时,m和n的二进制表示中肯定是高位部分相同,低位部分不同。值得一提的是,m到n之间连续的数字的二进制表示是通过+1实现1逐渐向高位翻转的(这并不重要),所以m和n不同的低位部分中,这些数字里可定存在数字在这些为上是0,所以所求结果在这些位(m和n不同的低位部分)都是0。那么m和n相同的高位部分呢?所有的数字的这些位都是一样的,所以按位与的结果还是它本身。

int rangeBitwiseAnd(int m, int n) {
        int i = 0;
        for(; m!=n; m>>=1,n>>=1) ++i;
        return m<<i;
    }

另外两道题目似乎与位运算无关,或者是我比较愚钝,没有找到位运算的方法去解题,所以就不列在这个总结里了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值