位运算技巧总结

一.    概念:位运算是指按二进制进行的运算,只能对整形树进行操作。

二.   基本运算符:

(1) & 与:两个相应的二进制位都为一,则该位结果为1,否则为0 如:0111 & 1011 = 0011

(2)| 或:两个相应的二进制位有一个为1,则该位结果为1,否则为0 如: 0111 | 1011 = 1111

(3)^ 异或:两个相应的二进制位相同则为零,否则为1 如: 0111^1011 = 1100

(4)~ 取反 : 一元运算符,将二进制位取反,即1变为0,0变为1 如:~0111 = 1000

(5)<<左移运算符:n<<k,将n的二进制左移k位,低位补0(n*2^k) 如:0111<<1 = 1110

(6)>>右移运算符:n>>k,将n的二进制右移k位,低位被舍弃,高位(左边)补0(n/2^k) 如:0111>>1 = 0011

注:其中~的结合方向自右至左,且优先级高于算术运算符,其余运算符的结合方向都是自左至右,且优先级低于关系运算符。所以在运算时,注意括号的利用。

三.   二进制奇技淫巧

1.集合应用

(1)在集合中: &表示交集,| 表示并集, ^ 表示对称差集,(1<<n)-1表示n的全集

(2)s&(1<<i) 是否为0表示 i是否是子集s中的一个元素(全集从0~n-1); 表示i+1是否是子集s中的一个元素(全集从1~n)

(3)for(int k = (n-1)&n;k;k = (k-1)&n) 从集合角度取n的所有子集

(4)求n个元素的所有子集

    while(cin>>n)
    {
        for(int i = 0; i<(1<<n); i++)//枚举所有子集
        {
            for(int j = 0; j<n; j++)//枚举所有元素判断是否在子集里
            {
                if(s&(1<<i))printf("%d",i+1);
            }
            printf("\n");
        }

    }
    return 0;

(5)求集合或者现有子集的所有子集

    while(cin>>n){
        for(int i = 0;i<(1<<n);i++){//所有子集
            cout<<"集合 "<<i<<" 的所有子集 :"<<endl;
            for(int k = i;k>0;k = (k-1)&i){//所有 现在子集 的 所有子集
                for(int j = 0;j<n;j++){//判断元素
                    if(k&(1<<j))cout<<j+1;
                }
                cout<<endl;
            }
        }
    }

(6)求不相邻元素的所有集合

        for(int i = 0;i<(1<<n);i++){
            if((i>>1)&i)continue;//i里面含有相邻元素,退出
            //对集合的处理
        }

(7)必须含有n个指定位置集合 的所有集合

    while(cin>>n){
        int m,k;
        int sum = 0;
        cin>>m;
        while(m--){//求最小指定集合
            cin>>k;
            sum = sum|(1<<(k-1));
        }
        for(int i = sum;i<(1<<n);i = (i+1)|sum){//在sum基础上求所有集合
               //操作
        }
    }

(8)必须不含有n个指定位置集合 的所有集合

    while(cin>>n){
        int m,k;
        int sum = 0;
        cin>>m;
        while(m--){
            cin>>k;
            sum = sum|(1<<(k-1));
        }
        sum = sum^((1<<n)-1);//^取最大集
        //cout<<sum<<endl;
        for(int i = sum;i>=0;i = (i-1)&sum){
            cout<<i<<endl;
            if(i==0)break;//一定要有,不然出现负数的位运算导致循环不断进行
        }
    }
(9) 找出二进制中恰好含有 k个1的所有数

         for (int mask = 0; mask < 1 << n; ) {

               int tmp = mask & -mask;

               mask = (mask + tmp) | (((mask ^ (mask + tmp)) >> 2) / tmp);

         }

2.其他应用

(1)O(1)时间检查n是否为2的幂次

    while(cin>>n){
            if((n&(n-1))==0)cout<<"yes"<<endl;
            else cout<<"no"<<endl;
    }
(2)一个数组里,所有的数字都出现两次,只有一个数字出现一次,找出这个数字
    while(cin>>n){
            int sum,a;
            for(int i = 0;i<n;i++){
                cin>>a;
                if(i==0)sum = a;
                else sum = sum^a;//所有数字异或起来-〉异或的性质:自己异或自己为0,0异或n = n;
            }
        cout<<sum<<endl;
     }


猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/80363779