LeetCode分类刷题(一):位操作(Bit Manipulation)

位运算(Bit Manipulation)一直是程序员面试中的一个必须准备的主题, 不过现在面试中位运算出现的次数并不多,主要原因还是位运算太考察技巧了,很多时候很难在短时间内想出来,所以作为面试的题目显得有点太花时间了。

位运算的主要思想是五种运算:与(&),或(|),异或(^),左移(<<),右移(>>)。
位运算的常用技巧如下:

  1. n &(n-1)能够消灭n中最右侧的一个1。
  2. 右移:除以2, 左移:乘以2。
  3. 异或性质:交换律,0^a=a, a^a=0;

LeetCode中关于位运算的题目有以下四种类型题:

(一)位操作之汉明距离(Hamming weight)相关题目:

(二)位操作之单个数(Single Number)相关题目:

(三)位操作之反转相关题目:

(四)位操作之数学相关题目:


(一)位操作之汉明距离(Hamming weight)相关题目:

191. 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.
  • 题目要求:编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
  • 题目分析:当然,可以利用for循环,统计整数二进制表达式32位中1的个数;但是,有一种更高效的方法:使用n&(n-1)的方法,去除bit位的最后1个1,发现有几个1,就循环几次n&(n-1),直到n等于0。
  • 题目解答:
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while(n){
            res++;
            n = n & (n-1);
        }
        return res;
    }
};

461. Hamming Distance

  • The Hamming distance between two integers is the number of positions at which the corresponding bits are different. Given two integers x and y, calculate the Hamming distance.
  • 题目要求:求两个数字之间的汉明距离,题目中解释的很清楚了,两个数字之间的汉明距离就是其二进制数对应位不同的个数。
  • 题目分析:较优化的方法是,直接将两个数字异或起来,然后遍历异或结果的每一位,统计为1的个数。
  • 题目解答:
class Solution {
public:
    int hammingDistance(int x, int y) {
        int res = 0, temp = x ^ y;
        while(temp){
            res++;
            temp = temp & (temp-1);
        }
        return res;
    }
};

477. Total Hamming Distance

  • The Hamming distance between two integers is the number of positions at which the corresponding bits are different. Now your job is to find the total Hamming distance between all pairs of the given numbers.
  • 题目要求:给定一个int数组,求出数组中所有数值对汉明距离中总和。
  • 题目分析:利用上一题的思路,这个题目中需要统计两两数值对的汉明距离之和,时间复杂度为O(n*n);但是,有一种更高效的方法:对于数组中所有数字的同一个bit位,统计这个bit位上出现的1的次数count,那么这个bit位上出现的0的次数n-count,则总的汉明距离为count*(n-count),n是数组中元素的个数。
  • 题目解答:
class Solution {
public:
    int totalHammingDistance(vector& nums) {
        int res = 0;
        for(int i = 0; i < 31; i++){
            int cnt = 0;
            for(auto n : nums){
                if(n & 1<

(二)位操作之单个数(Single Number)相关题目:

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?
  • 题目要求:给定一个整数数组,除了一个元素出现1次外,其他每个元素都会出现2次。 找到那个出现1次的整数。注意:时间复杂度必须是O(n),并且空间复杂度为O(1)
  • 题目分析:由于题目对时间复杂度和空间复杂度有严格要求,因此不能使用sort排序方法,也不能使用map结构,只能另辟蹊径。把数组中所有的数字都异或起来,则每对相同的数字都会得0,然后最后剩下来的数字就是那个只有1次的数字。
  • 题目解答:
class Solution {
public:
    int singleNumber(vector& nums) {
        int res = 0;
        for(auto i : nums){
            res ^= i;
        }
        return res;
    }
};

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?
  • 题目要求:给定一个整数数组,除了一个元素出现3次外,其他每个元素都会出现2次。 找到那个出现3次的整数。注意:时间复杂度必须是O(n),并且空间复杂度为O(1)
  • 题目分析:由于题目对时间复杂度和空间复杂度有严格要求,因此不能使用sort排序方法,也不能使用map结构,只能另辟蹊径。我们可以建立一个32位的数字,来统计每一位上1出现的个数,我们知道如果某一位上为1的话,那么如果该整数出现了三次,对3去余为0,我们把每个数的对应位都加起来对3取余,最终剩下来的那个数就是单独的数字。
  • 题目解答:
class Solution {
public:
    int singleNumber(vector& nums) {
        int res = 0;
        for(int i = 0; i < 32; i++){
            int temp = 0;
            for(auto n : nums){
                temp += (n>>i) & 1;
            }
            temp %= 3;
            res |= (temp<

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].
  • 题目要求:给定一个整数数组,除了两个元素各出现1次外,其他每个元素都会出现2次。 找到那两个出现1次的整数。
  • 题目分析:由于题目对时间复杂度和空间复杂度有严格要求,因此不能使用sort排序方法,也不能使用map结构,只能另辟蹊径。首先我们先把原数组全部异或起来,那么我们会得到一个数字,这个数字是两个不相同的数字异或的结果,我们取出其中任意一位为‘1’的位,为了方便起见,我们用 temp^(temp&(temp-1)) 来取出最右端为‘1’的位,然后和原数组中的数字挨个相与,那么我们要求的两个不同的数字就被分到了两个小组中,分别将两个小组中的数字都异或起来,就可以得到最终结果了。
  • 题目解答:
class Solution {
public:
    vector singleNumber(vector& nums) {
        vector res(2, 0);
        int temp = 0;
        for(auto n : nums){
            temp ^= n;
        }
        int flag = temp^(temp&(temp-1));
        for(auto n : nums){
            if(flag & n) res[0] ^= n;
            else res[1] ^= n;
        }
        return res;
    }
};

 

(三)位操作之反转相关题目:

190. 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 as00111001011110000010100101000000).
  • 题目要求:把一个无符号int数字,按二进制位反转过来。
  • 题目分析:通过移位操作,一位一位来处理。目的是反转,所以先处理最右位。最右位(末端)一个一个地往res的左边推。
  • 题目解答:
class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t res = 0;
        for(int i = 0; i < 32; i++){
            uint32_t temp = (n>>i) & 1;
            res |= (temp<<(31-i));
        }
        return res;
    }
};

476. Number Complement

  • Given a positive integer, output its complement number. The complement strategy is to flip the bits of its binary representation.
  • 题目要求:给一个正整数,输出其补码数。 补码策略是反转其二进制表示的位。
  • 题目分析:我们知道需要做的就是每个位反转一下就行了,但是反转的起始位置上从最高位的1开始的,前面的0是不能被反转的。所以,只需每次取出整数最右侧的bit位进行反转,统计数据,然后n向右移一位,循环执行,直到n等于0为止。
  • 题目解答:
class Solution {
public:
    int findComplement(int num) {
        int res = 0, i = 0;
        while(num){
            int temp = (num & 1) ^ 1;
            res |= (temp << i++);
            num = num >> 1;
        }
        return res;
    }
};

 

(四)位操作之数学相关题目:

201. Bitwise AND of Numbers Range

  • Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers in this range, inclusive.For example, given the range [5, 7], you should return 4.
  • 题目要求:给一个范围,返回这个范围中所有的数按位相与最后的结果。
  • 题目分析:看题感觉需要对所有的[m,n]范围内的数字进行遍历一遍吧。其实不需要的。数组的数字是连续的,那么m,n范围内的二进制表示的末尾相同位置一定会出现不同的0,1。只要找出m,n的左边起的最长相同的二进制头部即可。发现了规律后,我们只要写代码找到左边公共的部分即可。
  • 题目解答:
class Solution {
public:
    int rangeBitwiseAnd(int m, int n) {
        int res = 0, i = 0;
        while(m != n){
            m >>= 1;
            n >>= 1;
            i++;
        }
        res = m << i;
        return res;
    }
};

318. Maximum Product of Word Lengths

  • Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the two words do not share common letters. You may assume that each word will contain only lower case letters. If no such two words exist, return 0.
  • 题目要求:这道题给我们了一个单词数组,让我们求两个没有相同字母的单词的长度之积的最大值。
  • 题目分析:大神的方法,都是用了mask,因为题目中说都是小写字母,那么只有26位,一个整型数int有32位,我们可以用后26位来对应26个字母,若为1,说明该对应位置的字母出现过,那么每个单词的都可由一个int数字表示,两个单词没有共同字母的条件是这两个int数相与为0。
  • 题目解答:
class Solution {
public:
    int maxProduct(vector& words) {
        int n = words.size(), res = 0;
        vector mask(n, 0);
        for(int i = 0; i < n; i++){
            for(auto c : words[i]) mask[i] |= 1 << (c - 'a');
            for(int j = 0; j < i; j++){
                if((mask[i] & mask[j]) == 0){
                    res = max(res, int(words[i].size()*words[j].size()));
                }
            }
        }
        return res;
    }
};

371. Sum of Two Integers

  • Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -. Example:Given a = 1 and b = 2, return 3.
  • 题目要求:实现两数相加,但是不能用加号或者其他什么数学运算符号。
  • 题目分析:位操作实现加法,用异或算不带进位的和,用与并左移1位来算进位,然后把两者加起来即可。
  • 题目解答:
class Solution {
public:
    int getSum(int a, int b) {
        int sum = a;
        while(b){
            sum = a ^ b;
            b = (a & b) << 1;
            a = sum;
        }
        return sum;
    }
};

29. Divide Two Integers

  • Divide two integers without using multiplication, division and mod operator.If it is overflow, return MAX_INT.
  • 题目要求:这道题让我们求两数相除,而且规定我们不能用乘法,除法和取余操作。
  • 题目分析:位操作Bit Operation,思路是,如果被除数大于或等于除数,则进行如下循环,定义变量m等于除数,定义计数n,当m的两倍小于等于被除数时,进行如下循环,m扩大一倍,n扩大一倍,然后更新res和dvd。然后我们还要根据被除数和除数的正负来确定返回值的正负,这里我们采用长整型long long来完成所有的计算,最后返回值乘以符号即可。
  • 题目解答:
class Solution {
public:
    int divide(int dividend, int divisor) {
        long long dvd = abs((long long)dividend), dvs = abs((long long)divisor), res = 0;
        if (dvd < dvs) return 0;
        while(dvd >= dvs){
            long long m = dvs, n = 1;
            while(dvd > (m << 1)){
                m <<= 1;
                n <<= 1;
            }
            res += n;
            dvd -= m;
        }
        if((dividend < 0)^(divisor < 0)) res = -res;
        return res > INT_MAX ? INT_MAX : res;
    }
};

总结:

这篇总结介绍了LeetCode中几类关于位运算的题目,虽然数量不多,不过思想上还是都挺有意义的,如果面试中遇到能提出位运算的解法还是能加分不少,所以位运算在有些题目中还是一把关键的武器。 

如果各位看官们,大神们发现了任何错误,或是代码无法通过OJ,或是有更好的解法,或是有任何疑问,意见和建议的话,请一定要在帖子下面评论区留言告知博主啊,多谢多谢,祝大家刷得愉快,刷得精彩,刷出美好未来~

你可能感兴趣的:(LeetCode刷题,leetcode,位操作,程序员面试题,Bit,Manipulation)