• [专题六] 位运算


    位运算

    位运算的基本操作

    typedef struct Bitset {
        int setsize; // 16 32 ... int 是32位的
        int arraysize;  // 相当于有几行
        unsigned short *v; // 之后会分配一片连续的空间
    }Bitset; //还非要大写才行 ..
    
    // SetInit(size) 集合大小
    Bitset SetInit(int size) {
        Bitset s;
        s.setsize = size;
        s.arraysize = (size + 15) >> 4; // 右移4位 除以16
        //我之前好像智障了 sizeof后面的是不需要看的 肯定是要带上的 所以这行就是相当于 创建一个sizes长度的v 类型是unsigned short型
        s.v = (unsigned short*) malloc( size * sizeof(unsigned short));
        for (int i = 0; i < size; i++) {
            s.v[i] = 0; // 初始化
        }
        return s;
    }
    
    // SetAssign(A, B) 将集合B 复制 到集合A
    void SetAssign(Bitset A, Bitset B) {
        if(A.setsize!=B.setsize) exit(-1);
        for(int i = 0; i < A.arraysize; i++) A.v[i] = B.v[i]; //定义是按照n定,赋值是按照层d赋值
    }
    
    // SetMember(x, A) 判断是否是相同类型
    /*
     首先明确一点 就是集合A的元素{1,3,5} 它代表的是 第1、3、5位的数字为1 也就是 101010
     而元素x : 若 x=2 则表示判断 第x位是否为1。 现在假设传入的x=28 说明我要找的是第28位的数字
     x >> 4 = 1 说明我应该去v[1]找
     x & 15 = 12 相当于mod 16 = 12 , 再将 1 右移 12位 ,就是相当于 把v中第13个数 和 1 相与
     */
    int findIndex(int x) {
        return x >> 4; // 找下标
    }
    
    int bitIndex(int x) {
        return 1 << (x & 15);
    }
    
    int SetMember(int x, Bitset A)
    {
        if (x<0 || x > A.setsize) exit(-1);
        return A.v[findIndex(x)] & bitIndex(x);
    }
    
    // SetEqual(A, B) 两集合是否相等
    int SetEqual(Bitset A, Bitset B) {
        if(A.setsize!=B.setsize) exit(-1);
        for(int i = 0; i < A.arraysize; i++) {
            if(A.v[i]!=B.v[i])
                return 0;
        }
        return 1;
    }
    
    // SetUnion(A, B) U 并集合
    Bitset SetUnion(Bitset A, Bitset B) {
        Bitset C = SetInit(A.setsize);
        if(A.setsize!=B.setsize) exit(-1);
        for(int i = 0; i < A.arraysize; i++) {
            C.v[i] = A.v[i] | B.v[i];
        }
        return C;
    }
    
    // SetInterSection(A, B) 交集  ->  C.v[i] = A.v[i] | B.v[i];
    // SetDifference(A, B) 差集 属于A而不属于B的
    Bitset setDifference(Bitset A, Bitset B) {
        Bitset C = SetInit(A.setsize);
        if(A.setsize!=B.setsize) exit(-1);
        for(int i = 0; i < A.arraysize; i++) {
            C.v[i] = A.v[i] ^ (A.v[i] & B.v[i]); // 这步骤就是把,A B都有的元素取出,再异或一下,不同取1,就可以拿出差集了。
        }
        return C;
    }
    
    // SetInsert(x, S) 在x位插入元素
    Bitset SetInsert(int x, Bitset S) {
        if(x<0 || x > S.setsize) exit(-1);
        S.v[findIndex(x)] |= bitIndex(x);// 妈耶 我怎么会把它想那么复杂呢
        return S;
    }
    
    // SetDelete(x,S) 在x位删除元素
    Bitset SetDelete(int x, Bitset S) {
        if(x<0 || x > S.setsize) exit(-1);
        S.v[findIndex(x)] &= ~bitIndex(x); // x位为0,其余都是1,&上之后,可以成功删除
        return S;
    }
    
    

    一般操作

    // 求n的第k位数字: 
    n >> k & 1
    //返回n的最后一位1:
    lowbit(n) = n & -n
    

    逆转二进制数

    int rever(int num) {
        int m_1  = 0x55555555; // 0101 8对
        int m_2  = 0x33333333;  // 0011 8对
        int m_4  = 0x0f0f0f0f; // 00001111 4对
        int m_8  = 0x00ff00ff;
        int m_16 = 0x0000ffff;
        
        int b = ((num & m_16) << 16) + ((num >> 16) & m_16); // 右挪左,左侧右移16位
        int c = ((b & m_8) << 8) + ((b >> 8) & m_8);
        int d = ((c & m_4) << 4) + ((c >> 4) & m_4);
        int e = ((d & m_2) << 2) + ((d >> 2) & m_2);
        int f = ((e & m_1) << 1) + ((e >> 1) & m_1);
        return f;
    }
    int main() {
        int a = 67;
        int b = rever(a);
        int c = rever(b);
        printf("%d %d", a, c);
        return 0;
    }
    

    leetcode.762 二进制表示中质数个计算置位

    题目:给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,计算置位位数为质数的整数个数。(注意,计算置位代表二进制表示中1的个数。例如 21 的二进制表示 10101 有 3 个计算置位。还有,1 不是质数。)

    int countPrimeSetBits(int L, int R) {
      int res = 0;
      unordered_set<int> primes({2, 3, 5, 7, 11, 13, 17, 19});
      for (int i = L; i <= R; i ++ )
      {
        int s = 0;
        for (int j = i; j; j >>= 1) s += j & 1;
        if (primes.count(s)) res ++;
      }
      return res;
    }
    

    leetcode.231 2的整次幂

    题目:给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

    bool isPowerOfTwo(int n) {
      return n > 0 && ( n & -n ) == n;
    }
    

    leetcode.136 只出现一次的数字

    题目:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

    // 输入 [1,2,3,1,2,3,4]
    int singleNumber(vector<int>& nums) {
      int res = 0;
      for(auto x : nums) res ^= x;
      return res;
    }
    

    leetcode.136 只出现一次的数字II

    题目:除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
    题解:统计每一列1的个数,是3的倍数,表明 贡献了该位的数字是3的整数; 反之,如果一个数字只出现了一次,那么其贡献过的相应位置的1的总个数绝对不可能是三的倍数。最后将该列模3得到1后,右移相应位,可以转变成2进制上的相应位置为1。

    int singleNumber(vector<int>& nums) {
      int bit = 32, res = 0, count; // int型为32位整数
      for(int i = 0; i < bit; i ++) {
        count = 0;
        for(int j = 0; j < nums.size(); j ++) {
          count += ( nums[j] >> i ) & 1; // 上一次的移位本次是不会记录的 因此 每次都是移动i位
          /* 我之前是count+=(nums[j]&1);
          	 nums[j] >> 1; 这样显示是不行的。nums[j]的移动不可能保存到下一轮。所以是用i记录遍历到的位数
          	 真的是太牛逼了- -。
          */
        }
        if(count % 3 == 1) res += (1 << i);
      }
      return res;
    }
    

    leetcode.476 数字的补数

    题目:给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。

    int findComplement(int num) {
      int res = 0, bit = 0;
      while(num) {
        res += (!(num & 1) << bit);
        num >>= 1;
        bit++;
      }
      return res;
    }
    

    知识点和代码均学习于Acwing: https://www.acwing.com/activity/

  • 相关阅读:
    springboot拦截器的拦截配置和添加多个拦截器
    ASCII对照
    爬虫出现403错误解决办法
    PhantomJS在Selenium中被标记为过时的应对措施
    Selenium 之订制启动Chrome的选项(Options)
    Selenium+PhantomJS使用时报错原因及解决方案
    python爬虫之xpath的基本使用
    JSONObject类的引用必须jar包
    selenium之使用chrome浏览器测试(附chromedriver与chrome的对应关系表)
    PhantomJS 与python的结合
  • 原文地址:https://www.cnblogs.com/recoverableTi/p/12248052.html
Copyright © 2020-2023  润新知