《LeetCode》位运算详解

简介: 《LeetCode》位运算详解

位运算


^ : 按位异或
& : 按位与
| : 按位或
~ : 取反
<< : 算术左移
>> : 算术右移
0s和1s分别表示只由0或1构成的二进制数字


x ^ 0s == x;
x ^ 1s == ~x;
x ^ x == 0;
x & 0s == 0;
x & 1s == 1;
x & x == x;
x | 0s == x;
x | 1s == 1s;
x | x == x;


//n & (n-1)可以去除n的位级表示中最低的那一位
n = 11110100
n & (n-1) == 11110000  
//n & (-n)可以得到n的位级表示中最低的那一位
n = 11110100
n & (-n) == 00000100    


191. 位1的个数【简单】



题目:输入为长度32的二进制串


思路一:循环检查二进制位


时间复杂度:O(k),k为二进制位数,k=32


空间复杂度:O(1)


class Solution {
public:
  int hammingWeight(uint32_t n) {
    int ret = 0;
    for (int i = 0; i <= 31; i++) {
      if (n & (1 << i)) {   //检查每一位是否为1   从低位到高位检查
        ret++;
      }
    }
    return ret;
  }
};


思路二:位运算优化


时间复杂度:O(logn) 循环次数 = 1的个数


空间复杂度:O(1)


class Solution {
public:
  int hammingWeight(uint32_t n) {
    int ret = 0;
    while (n) {
      n = n & (n - 1);    //n & (n - 1)    每次把最低位的1  变成  0
      ret++;
    }
    return ret;
  }
};


461. 汉明距离【简单】



方法二:移位实现位计数



时间复杂度:O(logC),其中C是元素的数据范围,在本题中logC = 31


空间复杂度:O(1)


class Solution{
public:
    int hammingDistance(int x, int y){
        int diff = x ^ y;  //异或
        int res = 0;
        while(diff){
            res += diff & 1;  //看低位是否为1
            diff >>= 1;       //右移
        }
        return res;
    }
};


优化:0的位可以不判断


  • 方法:Brian Kernighan 算法


时间复杂度:O(m),m为1的位数


空间复杂度:O(1)


class Solution{
public:
    int hammingDistance(int x, int y){
        int diff = x ^ y;  //异或
        int res = 0;
        while(diff){
            diff &= diff - 1;  //从低位到高位将1变0
            res++;      
        }
        return res;
    }
};


190. 颠倒二进制位【简单】



  • 思路一:逐位颠倒


时间复杂度:O(logn),即循环32次


空间复杂度:O(1)


class Solution {
public:
  uint32_t reverseBits(uint32_t n) {
    uint32_t res = 0;
    for (int i = 0; i <= 31; i++) {
      res <<= 1;     //扩充一位
            res |= n & 1;  //扩充位上填数
            n >>= 1;      //舍弃低位
    }
    return res;
  }
};


优化:不记0位


class Solution {
public:
  uint32_t reverseBits(uint32_t n) {
    uint32_t rev = 0;
    for (int i = 0; i <= 31 && n > 0; i++) {
      rev = rev | ((n & 1) << (31 - i));     //|相当于保留1
      n >>= 1;                               //n右移一位
    }
    return rev;
  }
};


思路二:分而治之



时间复杂度:O(1)


空间复杂度:O(1)


class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        n = (n >> 16) | (n << 16);
        n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8);   // 00000000111111110000000011111111
        n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4);   // 00001111000011110000111100001111
        n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2);   // 00110011001100110011001100110011
        n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1);   // 01010101010101010101010101010101
        return n;
    }
};


136. 只出现一次的数字【简单】



思路一:哈希表计数


时间复杂度:O(n)


空间复杂:O(n)


思路二:异或运算


时间复杂度:O(n) n为数组长度


空间复杂度:O(1)



  • [x]


class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (auto e: nums) ret ^= e;      //两个相同的值做异或结果为0  任何数和0异或等于它本身
        return ret;
    }
};


一个多余空间都不用


class Solution {
public:
  int singleNumber(vector<int>& nums) {
    for (int i = 1; i < nums.size(); i++) {
      nums[0] ^= nums[i];
    }
    return nums[0];
  }
};


260. 只出现一次的数字 III【中等】



思路一:哈希表


时间复杂度:O(n)


空间复杂度:O(n)


  • 思路二:位运算


时间复杂度:O(n)


空间复杂度:O(1)


思路:



class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unsigned int target = 0;   //unsigned不能少
        // 一对相同的数字异或为0
        // 所以target为两个只出现了一次的元素的异或
        for (auto n: nums) {
            target ^= n;
        }
        // 因为两数不同,lowbit必然不为0
        // 物理意义就是两个不同的数字不同的最低的位在哪
        int lowbit = target & (-target);
        int a1 = 0;
        int a2 = 0;
        // 重复上述的过程,但是将nums按照lowbit为1或者为0分类
        // 则两个数必然被分到不同的类目;而相同的数字一定在同一个类目
        // 所以按类目分别异或就可以得到两个不同的数字
        for (auto n: nums) {
            if (n & lowbit) {
                a1 ^= n;
            } else {
                a2 ^= n;
            }
        }
        return {a1, a2};
    }
};


231. 2 的幂【简单】



思路一:一直÷2,除到不能除再判断是否为1


时间复杂度:O(logn)


空间复杂度:O(1)


思路四:(2^30) % n ==0?


//时间复杂度:O(1)
//空间复杂度:O(1)
class Solution {
//private:
  //static constexpr int BIG = 1 << 30;   //最大的2的幂数  即BIG=2^30
public:
  bool isPowerOfTwo(int n) {
    return n > 0 && 1073741824 % n == 0;
  }
};


  • 思路二:位运算


时间复杂度:O(1)


空间复杂度:O(1)


思路:2的幂的二进制只有一位是1,其他全是0


class Solution {
public:
  bool isPowerOfTwo(int n) {
    return n > 0 && (n & (n - 1)) == 0;   //抹掉1,判断是否等于0
  }
};


版本二:


class Solution {
public:
  bool isPowerOfTwo(int n) {
    return n > 0 && (n & -n) == n;    //提取1,判断是还等于n,等于说明原来的数只有一个1  
  }
};


342. 4的幂【简单】


类似上一题


思路:n>0 && 1的位数只有一位 && 出现在奇数位上


时间复杂度:O(1)


空间复杂度:O(1)


bool isPowerOfFour(int n){
  return n > 0 && !(n & (n - 1)) && (n & 1431655765);  //与10101..101的十进制数按位与
};


class Solution {
public:
    bool isPowerOfFour(int n) {
        return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
    }
};


思路二:4的幂 % 3 == 1


时间复杂度:O(1)


空间复杂度:O(1)


class Solution {
public:
    bool isPowerOfFour(int n) {
        return n > 0 && (n & (n - 1)) == 0 && n % 3 == 1;
    }
};


318. 最大单词长度乘积【中等】



思路一:双for暴力破解,写一个判断word是否有相同字母的函数


  • 思路二:位运算


时间复杂度:O(n^2 + L)


空间复杂度:O(n)


class Solution {
public:
    int maxProduct(vector<string>& words) {
    unordered_map<int, int> map;
        int res = 0;
        for(const string& word : words){
      int mask = 0;
            int size = word.size();
            for(const char& c : word){ //求到该单词对应的长度为26的二进制数字
                mask |= 1 << (c - 'a');
            }
            map[mask] = max(map[mask], size); //max(ab, aab)  最优,避免了重复计算
            for(const auto& [h_mask, h_len] : map){
                if(!(mask & h_mask)){ //如果两个字符串含有重复数字,它们的二进制表示按位与不为0
                    res = max(res, size * h_len);
                }
            }
        }
        return res;
    }
};



338. 比特位计数【简单】



思路一:分别计算每一个i的1比特位


时间复杂度:O(nlogn)


空间复杂度:O(n)


class Solution {
public:
    int count (int i){
        int res = 0;
        while(i){
            i &= (i - 1);
            res++;
        }
        return res;
    }
    vector<int> countBits(int n) {
        vector<int> res(n+1);
        for(int i = 0; i <= n; i++){
            res[i] = count(i);
        }
        return res;
    }
};


  • 思路二:动态规划


时间复杂度:O(n)


空间复杂度:O(n)


class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> bits(n + 1);
        for (int i = 1; i <= n; i++) {
            bits[i] = bits[i & (i - 1)] + 1;
        }
        return bits;
    }
};


268. 丢失的数字【简单】



class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int res = 0;
        int n = nums.size();
        for (int i = 0; i < n; i++) {
            res ^= nums[i];
        }
        for (int i = 0; i <= n; i++) {
            res ^= i;
        }
        return res;
    }
};


389. 找不同【简单】



思路一:哈希表


时间复杂度:O(n)


空间复杂度:O(n)


  • 思路二:位运算


时间复杂度:O(n)


空间复杂度:O(1)


class Solution {
public:
    char findTheDifference(string s, string t) {
        int res = 0;
        for(auto i : s)
            res ^= i;
        for(auto i : t)
            res ^= i;
        return res;
    }
};


思路三:求和


class Solution {
public:
  char findTheDifference02(string s, string t) {
    int s_sum = 0;
    int t_sum = 0;
    for (char ch : s) 
      s_sum += ch;
    for (char ch : t)
      t_sum += ch;
    return t_sum - s_sum;
  }
};  


693. 交替位二进制数【简单】



class Solution {
public:
    bool hasAlternatingBits(int n) {
        long a = n ^ (n >> 1);  //如果n交替,则a的二进制全为1
        return (a & (a + 1)) == 0;
    }
};


476. 数字的补数【简单】




class Solution {
public:
    int findComplement(int num) {
        long a = 1;
        while(true){
            if(num >= a){
                a <<= 1; // 等价于 a *= 2;
            }else{
                return a - num - 1;
            }
        }
    }
};
相关文章
|
3月前
|
存储 算法 程序员
【Leetcode 程序员面试金典 01.01】判定字符是否唯一 —— 位运算|哈希表
可以使用哈希表或位运算来解决此问题:由题可知s[i]仅包含小写字母,int[26]即能表示字符的出现次数;
|
3月前
|
Java 程序员
【Leetcode 程序员面试金典 05.01】插入 —— 位运算
位运算问题,只需要把 N 的 i 到 j 位都置 0 后再和 M 左移 i 位的结果进行按位或即可
|
8月前
|
算法 C语言 C++
【位运算问题】Leetcode 136、137、260问题详解及代码实现
此外,任意一个数异或0都为他本身 (这从二进制编码来理解也很好理解,0的二进制编码全为0,任意一个数与其异或不同的就是若干位的1)
54 0
|
算法
leetcode-每日一题1217. 玩筹码(贪心+位运算)
判断元素的奇偶性,把奇数下标记录在odd 元素里
58 0
leetcode-每日一题1217. 玩筹码(贪心+位运算)
|
缓存 Java 程序员
|
算法 C++ Python
经典位运算算法模板-附LeetCode剑指 Offer 56 - I. 数组中数字出现的次数-题解-python && C++源代码
经典位运算算法模板-附LeetCode剑指 Offer 56 - I. 数组中数字出现的次数-题解-python && C++源代码
|
C++ Python
LeetCode每日一题题解:693. 交替位二进制数-题解-python && C++源代码-经典位运算
LeetCode每日一题题解:693. 交替位二进制数-题解-python && C++源代码-经典位运算
|
机器学习/深度学习 存储
【LeetCode剑指offer65】不用加减乘除做加法(位运算)
(a & b) << 1能够计算a和b的所有进位值; a ^ b则是计算a和b的各位相加(不管进位值)
97 0
【LeetCode剑指offer65】不用加减乘除做加法(位运算)
LeetCode 2032. 至少在两个数组中出现的值(哈希/位运算)
LeetCode 2032. 至少在两个数组中出现的值(哈希/位运算)
108 0