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<int>& nums) {
        int res = 0;
        for(int i = 0; i < 31; i++){
            int cnt = 0;
            for(auto n : nums){
                if(n & 1<<i) cnt++;
            }
            res += cnt * (nums.size() - cnt);
        }
        return res;
    }
};

(二)位操作之单个数(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<int>& 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<int>& 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<<i);
        }
        return res;
    }
};

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<int> singleNumber(vector<int>& nums) {
        vector<int> 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<string>& words) {
        int n = words.size(), res = 0;
        vector<int> 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,或是有更好的解法,或是有任何疑问,意见和建议的话,请一定要在帖子下面评论区留言告知博主啊,多谢多谢,祝大家刷得愉快,刷得精彩,刷出美好未来~

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: "public class"是Java编程语言中的一个关键字组合,用于定义一个公共类。在Java中,每个类都必须在一个文件中定义,并且文件名必须与类名相同。一个Java源代码文件中可以包含多个类定义,但只能有一个公共类定义。公共类是指可以被其他类和程序访问的类。当一个类被声明为public时,它可以在同一个包或其他包中被其他类使用。公共类的定义通常以以下格式开始: ``` public class ClassName { // Class definition } ``` 其中,`ClassName`是类的名称,紧随其后的是类的定义代码。 ### 回答2: public class 是Java中的关键字,用于定义一个公共的类。在Java中,类是一种封装数据和方法的机制,具有相似属性和行为的对象可以通过类来创建。 public意味着该类是公共的,即可以被其他类访问和使用。公共类可以在同一个包内的其他类中创建对象,并调用该类中的方法或访问该类中的属性。同时,公共类也可以被不同包中的类访问和使用。 class表示该关键字后面紧跟着的是一个类的定义。类的定义包括类的名称、类的属性和类的方法。其中,类的名称用于标识该类的身份,属性用于描述类的状态,方法用于定义类的行为。 通过使用public class关键字,我们可以创建一个公共类,该类可以被其他类访问和使用。在创建对象时,可以使用该类的名称来实例化该类,从而使用该类中定义的属性和方法。公共类的使用可以有效地实现代码的复用和模块化开发,提高代码的可读性和可维护性。 ### 回答3: public class是Java语言中的一个关键字,用于定义一个公有的类。在Java中,类是面向对象的基本概念之一,被用来创建对象。public关键字用于指定类的访问权限,public表示该类可以被其他类访问和使用。 使用public class关键字定义的类可以在程序的不同部分被访问到,包括其他类、同一包中的类以及其他项目。一般情况下,公有类会被写在单独的文件中,文件名必须与类名相同。 公有类通常具有以下特点: 1. 可以被程序的任何部分访问和使用; 2. 可以被其他类继承; 3. 可以在不同的包中被引用和调用。 需要注意的是,一个Java程序中只能有一个公有类,并且该类的名称必须与所在文件名相同。公有类中的方法和属性也可以被公有类之外的其他类使用,但非公有类中的方法和属性只能被同一包中的其他类使用。 总之,public class关键字用于定义一个公有的类,具有公有类的访问权限,可以被其他类访问和使用,是Java面向对象编程中的基本概念之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值