算法——位运算

Posted czc1999

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法——位运算相关的知识,希望对你有一定的参考价值。

&:按位与。
|  :按位或。
~ :按位取反,带符号位。(注意和!的区别,!只是逻辑取反) 
^ : 异或也叫半加运算:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0。
>> : 表示右移,算数右移,如果该数为正,高位补符。
>>>:表示无符号右移,也叫逻辑右移,高位补零。

小操作:                             


   1. 判断奇偶数

  a & 1

  2. 交换变量

a ^= b, b ^= a, a ^= b

  3. 乘以(除以)2的n次方:左移,右移;取模运算:a % (2 ^ n) 等价于 a & (2 ^ n - 1)

  4. 判断一个整数是不是2的次方

如果是2的次方,那么这个数的二进制表示中一定只有一个1。

技术分享图片

使用的时候注意: 
return ((x&(x - 1)) == 0) && (x != 0);

   5. 取int型变量a的第k位(>>的优先级较高,但用的时候还是尽量加)

(a >> k) & 1

  6. 将int型变量a的第k位置0 

  a = a & ~(1 << k)

  7. 将int型变量a的第k位置1

  a = a | (1 << k)

  8. int型变量循环左移k次(取int类型为4个字节)

  a = a << k | a >> 32 - k 

  9. int型变量a循环右移k次

  a = a >> k | a << 32 - k

  15. x 的相反数

  (~x + 1) (跟整型数的补码存储有关)

在网上也看到有说可以用 x | 0 实现 浮点数向下取整,就是floor函数的功能。

位运算是整型数的特权,但这也和语言特性有关,像JAVA,C/C++都是整型和浮点型储存形式完全不一样。

javascript内部,所有数值类型都是按64为浮点数存储,所以也就有了 1.8 | 0 这种操作(当进行位运算的时候,会自动舍弃小数位,转换为32位有符号的整数)。

 例题:                      


 

  • 一个无序数组里有若干个正整数,范围从1到100,其中99个整数都出现了偶数次,只有一个整数出现了奇数次(比如1,1,2,2,3,3,4,5,5),如何找到这个出现奇数次的整数?

int findFigure(int* array,int length) {
    int result = 0;
    for (int i = 0; i < length; i++) {
        result ^= array[i];
    }
    return result;
}
  • 一个无序数组里有若干个正整数,范围从1到100,其中98个整数都出现了偶数次,只有两个整数出现了奇数次(比如1,1,2,2,3,4,5,5),如何找到这两个出现奇数次的整数?

遍历整个数组,依次做异或运算。结果等于这两个整数的异或结果。这个结果中,至少会有一个二进制位是1(如果都是0,说明两个数相等,和题目不符)。

举个例子,如果最终异或的结果是5,转换成二进制是00000101。此时我们可以选择任意一个是1的二进制位来分析,比如末位。

我们将数组根据末位的不同分成两部分,再将这两部分 分别依次进行异或,最终得到的两个结果就是这两个数。

int findFigure(int* a, int length) {
    int result = 0;
    for (int i = 0; i < length; i++) {
        result ^= a[i];
    }
    return result;
}

void find(int *a,int length) {
    int *x = new int[length], *y = new int[length];
    int result = findFigure(a, length);
    int sign = 0;
    //位运算符优先级高于赋值符号
    while ((result & 1) != 1) {
        result >>= 1;
        sign++;
    }
    int j = 0, k = 0;
    for (int i = 0; i < length; i++) {
        if ((a[i] >> sign) & 1) {
            x[j++] = a[i];
        }
        else {
            y[k++] = a[i];
        }
    }
    cout << findFigure(x, j) << endl;
    cout << findFigure(y, k) << endl;
    delete[]x;
    delete[]y;
}

技术分享图片

这题看到的时候,考虑了一下,不能用类似上面的方法,用32位数组记录3*n-1个数,每一位出现的次数,因为有n-1个数是三个,有1个数是两个。那1个数的 二进制 为1的位,在数组中一定是3*k+2,
也就是不为3的倍数。
数据量大到一定级别,不能再用cin会超时,这里限8000ms,用cin还是超时了。
技术分享图片
#include<iostream>
#include <math.h>
using namespace std;

int sign[32];
int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 0; i < 3 * n - 1; i++) {
            int t;
            //cin >> t;
            scanf("%d",&t);
            for (int k = 0; k < 32; k++) {
                if ((t >> k & 1)) {
                    sign[k]++;
                }
            }
        }
        int result = 0;
        for (int i = 0; i < 32; i++) {
            if (sign[i] % 3 != 0) {
                result += pow(2, i);
            }
        }
        cout << result << endl;
        memset(sign, 0, sizeof(int) * 32);
    }
    return 0;
}
View Code

PS:写的时候,没考虑负数,关键是测试数据里还没有 -_- ,我也没发现。

改了一下,result用 第k位 置1,这样负数也能处理了。

#include<iostream>
#include <math.h>
using namespace std;


int sign[32];
int main() {
    int n;
    cin >> n;
        for (int i = 0; i < 3 * n - 1; i++) {
            int t;
            //cin >> t;
            scanf("%d",&t);
                for (int k = 0; k < 32; k++) {
                    if ((t >> k & 1)) {
                    sign[k]++;
                }
            }
        }
        int result = 0;
            for (int i = 0; i < 32; i++) {
                if (sign[i] % 3 != 0) {
            //将result的第i位置1 result
|= (1 << i); } } cout << result << endl; return 0; }

 

 

 




以上是关于算法——位运算的主要内容,如果未能解决你的问题,请参考以下文章

算法进阶:0x01 位运算

0x01 基本算法-位运算 a^b

数据结构与算法-位运算

算法-位运算加法

位运算的操作与算法

数字位运算操作与算法简单示例