算法——位运算
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; }
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; }
以上是关于算法——位运算的主要内容,如果未能解决你的问题,请参考以下文章