位运算总结

总结一些简单的位运算的操作并利用位运算来解决一些问题,注意: &, |, ^在进行位运算时是将操作数的符号位也考虑在内的。

  1. 求解整数的绝对值
int abs(int a) {
    return (a ^ (a>>31)) - (a>>31);
}

int abs(int a) {
    return (a + (a>>31))^(a>>31);
}
  1. 求两个数的最大值
int max(int a, int b) {
    return (b&((a-b)>>31)) | (a&(~(a-b)>>31));
}

int max(int a, int b) {
    return x ^ ((x ^ y) & -(x < y));
}
  1. 求两个数的最小值
int min(int a, int b) {
    return (b&(~(a-b)>>31)) | (a&((a-b)>>31));
}

int min(int a, int b) {
    return y ^ ((x ^ y) & -(x < y));
}
  1. 求解两个数的和
int add(int a, int b) {
    if(a == 0) return b;
    return add((a&b)<<1, a^b);
}
  1. 判断两个数的符号是否相同
bool is_same_sign(int a, int b) {
    return (a ^ b) >= 0;
}
  1. 求解两个数的平均值
int aver(int a, int b) {
    return ((a^b)>>1) + (a&b);
}
  1. n+1, n-1, -n
n+1 : -~n
n-1 : ~-n
-n  : ~n + 1

结合所学利用位运算求解一些问题。

问题1:
求解一个整数数组中出现偶数次的数,要求时间复杂度位O(n),空间复杂度为O(1),其中整数的取值范围位0~63.

解题思路:不难想到利用异或的运算性质来进行,同一个数的偶数次运算之后的结果为0.

typedef long long ll;

void print(int arr[], int n) {
    ll _xor = 0;
    for(int i = 0; i < n; ++i) {
        _xor ^= (1<<arr[i]);
    }
    for(int i = 0; i < n; ++i) {
        if(!(_xor & (1LL<<arr[i]))) {
            cout << arr[i] << " ";
            _xor ^= (1LL<<arr[i]);
        }
    }
    cout << endl;
    return ;
}

int main() {

    //int arr[] = { 9, 12, 23, 10, 12, 12, 15, 23, 14, 12, 15 };
    int arr[] = { 1, 1, 1, 2, 2, 3 };
    int n = sizeof(arr) / sizeof(int);
    print(arr, n);

    return 0;
}

问题2:
利用位运算实现CRC中发送端的编码和接收端的差错检测等,主要实现过程为modulo-2 binary division
Cyclic Redundancy Check and Modulo-2 Division

问题3:
Copy set bits in a range
解题思路:利用与或位运算实现简单的位拷贝的操作。

问题4:
Check if a number is Bleak
解题思路:如果非负整数类型位int型,则我们枚举set bit count的范围为[1,31](long long 为[1,63]),然后判断n-countBits是否包含countBits个1,上述过程利用位运算可以简单地实现,注意上述的所有操作均在非负整数的范围内。
基本操作:计算整数二进制表示中1的个数。

int count_bit(int x) {
    int ret = 0;
    for( ; x; ret++, x&=(x-1));
    return ret;
}

问题5:
Count strings with consecutive 1’s
解题思路:计算n位二进制数的表示中存在连续1的数的个数,总的数的个数位2^n,对于存在连续1的数的个数不好计算,但是容易计算不存在连续1的个数的数。此处我们采用动态规划的方法求解方案数。
dp[i][0]:表示第i位为0的数的个数
dp[i][1]:表示第i位为1的数的个数
dp[i][0] = dp[i-1][0] + dp[i-1][1]
dp[i][1] = dp[i-1][0]
因此最终答案为(2^n)-dp[n][0]-dp[n][1].

问题6:
Gray to Binary and Binary to Gray conversion

解题思路:格雷码和二进制码的相互转换。
n位格雷码可以由n-1位格雷码生成

  • Let the list of (n-1)-bit Gray codes be L1. Create another list L2 which is reverse of L1.
  • Modify the list L1 by prefixing a ‘0’ in all codes of L1.
  • Modify the list L2 by prefixing a ‘1’ in all codes of L2.
  • Concatenate L1 and L2. The concatenated list is required list of n-bit Gray codes.
    Binary to Gray conversion :
  • The Most Significant Bit (MSB) of the gray code is always equal to the MSB of the given binary code.
  • Other bits of the output gray code can be obtained by XORing binary code bit at that index and previous index.
    Gray to binary conversion :

  • The Most Significant Bit (MSB) of the binary code is always equal to the MSB of the given binary number.

  • Other bits of the output binary code can be obtained by checking gray code bit at that index. If current gray code bit is 0, then copy previous binary code bit, else copy invert of previous binary code bit.

问题7:
Find the maximum subset XOR of a given set

解题思路:Maximization with xor operator

#include <iostream>

using namespace std;

const int INT_BITS = 32;
const int INT_MIN  = -(1LL<<31);

int maxSubSetXOR(int arr[], int n) {
    int index = 0;
    for(int i = INT_BITS - 1; i >= 0; --i) {
        int maxELE = INT_MIN, maxind;
        for(int j = index; j < n; ++j) {
            if((arr[j]&(1LL<<i)) != 0 && maxELE < arr[j]) {
                maxELE = arr[j];
                maxind = j;
            }
        }
        if(maxELE <= INT_MIN) continue;
        swap(arr[index], arr[maxind]);
        maxind = index;
        for(int j = 0; j < n; ++j) {
            if(j != maxind && (arr[j]&(1LL<<i)) != 0) {
                arr[j] = (arr[j]^arr[maxind]);
            }
        }
        index++;
    }
    int res = 0;
    for(int i = 0; i < n; ++i) {
        res = res ^ arr[i];
    }
    return res;
}

int main() {
    int arr[] = { 9, 8, 5 };
    //int arr[] = { 2, 4, 5 };
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << maxSubSetXOR(arr, n) << endl;
    return 0;
}

问题8:
Given a set, find XOR of the XOR’s of all subsets.

解题思路:对于元素个数为1的集合,结果为相应元素的值,当集合中元素的个数大于1时结果为0,根据异或的性质求解即可。

问题9:
Sum of Bitwise And of all pairs in a given array

解题思路:求解一整数数组中所有元素对与操作的和。暴力方法O(n^2)显然是不可行的。假设整数类型为int,我们考虑第i为对最终结果的贡献,第i位为1当且仅当进行与操作的两个运算数的第i位均为1,算法复杂度为O(32*n)。

问题10:
Find Next Sparse Number
解题思路:随意找几个数YY一下,便可发现求解方法。
例如44的二进制表示为101100,要消除101100中的连续的1且保证>=44,转换如下:1**011**00->1**100**00,发现仍然存在两个连续的1,继续进行上述操作**11**0000->**100**0000,于是最终结果为64。

问题11:
Find the maximum subarray XOR in a given array

解题思路:区间异或最大和转化成为前缀和数组中两个数的最大异或值。
求解一个数组中的两个数的最大异或值可以采用二进制字典树进行求解,具体见代码。

const int INT_BITS = 32;
const int MAXN = 1000010;

struct TrieNode {
    int val;
    TrieNode *next[2];
    TrieNode() {
        val = 0;
        for(int i = 0; i < 2; ++i) next[i] = NULL;
    }
}trie[MAXN], *root;
int tid;

void init_trie() {
    tid = 0;
    root = &trie[tid++];
}

void insert_trie(int x) {
    TrieNode *tmp = root;
    for(int i = INT_BITS; i >= 0; --i) {
        int flag = (x&(1LL<<i)) > 0;
        if(tmp->next[flag] == NULL) {
            tmp->next[flag] = &trie[tid++];
        }
        tmp = tmp->next[flag];
    }
    tmp->val = x;
    return ;
}

int get_max(int x) {
    TrieNode *tmp = root;
    for(int i = INT_BITS; i >= 0; --i) {
        int flag = ((x&(1LL<<i)) > 0);
        if(tmp->next[flag^1] != NULL) {
            tmp = tmp->next[flag^1];
        } else {
            tmp = tmp->next[flag];
        }
    }
    return max((x^tmp->val), x);
}

int solve(int arr[], int n) {
    int res = arr[0];
    int pre_xor = arr[0];
    init_trie();
    insert_trie(pre_xor);
    for(int i = 1; i < n; ++i) {
        pre_xor ^= arr[i];
        res = max(res, get_max(pre_xor));
        insert_trie(pre_xor);
    }
    return res;
}

int main() {
    int arr[] = { 8, 1, 2, 12 };
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("%d\n", solve(arr, n));
    return 0;
}

HRBUST 1717

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 1500010;
const int BITS = 40;

struct TrieNode {
    ll val;
    TrieNode *next[2];
    TrieNode() {
        val = 0;
        for(int i = 0; i < 2; ++i) next[i] = NULL;
    }
}*root;
ll  arr[100010];
int n;

void init_trie() {
    root = new TrieNode();
}

void insert_trie(ll x) {
    TrieNode *tmp = root;
    for(int i = BITS - 1; i >= 0; --i) {
        int flag = (x&(1LL<<i)) > 0;
        if(tmp->next[flag] == NULL) {
            tmp->next[flag] = new TrieNode();
        }
        tmp = tmp->next[flag];
    }
    tmp->val = x;
    return ;
}

ll get_max(ll x) {
    TrieNode *tmp = root;
    for(int i = BITS - 1; i >= 0; --i) {
        int flag = (x&(1LL<<i)) > 0;
        if(tmp->next[flag^1] != NULL) {
            tmp = tmp->next[flag^1];
        } else {
            tmp = tmp->next[flag];
        }
    }
    return max(x, x^(tmp->val));
}

ll solve() {
    ll res = 0;
    ll pre_xor = 0;
    ll suf_xor = 0;
    init_trie();
    for(int i = 0; i < n; ++i) {
        pre_xor ^= arr[i];
        res = max(res, pre_xor);
        insert_trie(pre_xor);
    }
    for(int i = n - 1; i >= 0; --i) {
        suf_xor ^= arr[i];
        res = max(res, get_max(suf_xor));
    }
    return res;
}

int main() {

    //freopen("aa.in", "r", stdin);
    while(scanf("%d", &n) != EOF) {
        for(int i = 0; i < n; ++i) {
            cin >> arr[i];
        }
        cout << solve() << endl;
    }
    return 0;
}

问题12:
Find XOR of two number without using XOR operator

问题13:
Sum of bit differences among all pairs
解题思路:同样按位计算总的次数即可,假设每个数均为int型,然后从[0,31]区间的位依次进行计算。注意(1,2)和(2,1)是两种不同的整数对。

int sumBitDifferences(int arr[], int n)
{
    int ans = 0;  // Initialize result

    // traverse over all bits
    for (int i = 0; i < 32; i++)
    {
        // count number of elements with i'th bit set
        int count = 0;
        for (int j = 0; j < n; j++)
            if ( (arr[j] & (1 << i)) )
                count++;

        // Add "count * (n - count) * 2" to the answer
        **ans += (count * (n - count) * 2);**
    }

    return ans;
}

问题14:
Check if a given number is sparse or not
解题思路:判断 n&(n>>1)

问题15:
Euclid’s Algorithm when % and / operations are costly

解题思路:采用位运算的方法来重新实现欧几里德算法。

// Efficient C++ program when % and / are not allowed
int gcd(int a, int b)
{
    // Base cases
    if (b == 0 || a == b) return a;
    if (a == 0) return b;

    // If both a and b are even, divide both a
    // and b by 2.  And multiply the result with 2
    if ( (a & 1) == 0 && (b & 1) == 0 )
       return gcd(a>>1, b>>1) << 1;

    // If a is even and b is odd, divide a bb 2
    if ( (a & 1) == 0 && (b & 1) != 0 )
       return gcd(a>>1, b);

    // If a is odd and b is even, divide b bb 2
    if ( (a & 1) != 0 && (b & 1) == 0 )
       return gcd(a, b>>1);

    // If both are odd, then apply normal subtraction 
    // algorithm.  Note that odd-odd case always 
    // converts odd-even case after one recursion
    return (a > b)? gcd(a-b, b): gcd(a, b-a);
}

问题16:
Find n’th Magic Number
解题思路: 将n进行二进制表示后,例如1011 = 5^4 + 5^2 + 5^1,进行处理即可。

问题17:
Subtract two numbers without using arithmetic operators

int sub(int x, int y) {
    while(y != 0) {
        int borrow = (~x)&y;
        x ^= y;
        y = (borrow<<1);
    }
    return x;
}

int sub2(int x, int y) {
    if(y == 0) return x;
    return sub2(x^y, ((~x)&y)<<1);
}

问题18
Check if a number is multiple of 9 using bitwise operators

bool isDivBy9(int x) {
    if(x == 0 || x == 9) return true;
    if(x < 9) return false;
    return isDivBy9((int)(x>>3)-(int)(x&7));
}

问题19
Find the element that appears once
解题思路:第一种方法值得研究,第二种方法按位处理很容易想到。

问题20
Count total set bits in all numbers from 1 to n
解题思路:数位DP。

#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int cal_len(int x) {
    if(x == 0) return 1;
    int len = 0;
    while(x) x >>= 1, len++;
    return len;
}

int f(int n) {
    if(n == 0) return 0;
    return n * (1<<(n-1));
}

int solve(int n, int pos, int num) {
    if(pos < 0) return num;
    int res = 0;
    for(int i = 0; i < !!(n&(1<<pos)); ++i) {
        res += num * (1<<pos);
        res += f(pos);
    }
    if(n&(1<<pos)) {
        res += solve(n, pos - 1, num + 1);
    } else {
        res += solve(n, pos - 1, num);
    }
    return res;
}

int main() {
    int n;
    while(cin >> n) {
        cout << solve(n, cal_len(n)-1, 0) << endl;
    }
    return 0;
}

问题21:
Swap bits in a given number
解题思路:运用 a^(a^b) = b,b^(a^b)=a来实现两部分的位交换。

int swapBits(unsigned int x, unsigned int p1, unsigned int p2, unsigned int n)
{
    /* xor contains xor of two sets */
    unsigned int xor = ((x >> p1) ^ (x >> p2)) & ((1U << n) - 1);

    /* To swap two sets, we need to again XOR the xor with original sets */
    return x ^ ((xor << p1) | (xor << p2));
}

问题22:
Next higher number with same number of set bits
解题思路:方法很容易想到,用位运算的方法来描述这个操作过程。

int cal_next_num(int x) {
    int a = x & (-x);
    int b = x + a;
    int c = (x^b) / a;
    int d = c >> 2;
    return b|d;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值