力扣刷题c++位运算总结

1、位运算基础

(1)概述:
原码:补码:反码:反码等于补码减1;
按位与(&)、按位或(| )、按位异或(^)、取反(~)、左移(<<)、右移(>>),只能用于整型操作数,即用于带符号或无符号的 char、short、int 与 long 类型。
(2)按位与
0&0=0; 0&1=0; 1&0=0; 1&1=1,负数按补码形式参加按位与运算;都真为1
应用例子:
判断奇偶:if((n&1) == 0)
取出一个数中指定位:111 & 011 取第2、3位;
清零:和0按位与;
(3)按位或
0|0=0; 0|1=1;1|0=1;1|1=1; 有1出1;
(4)按位异或
0 ^ 0=0; 0 ^ 1=1; 1^ 0=1; 1^1=0;不同为1;等同于不进位的加法;
应用例子:
使特定位翻转:x=101 10,使x低2位翻转,用x^ 00011 = 101 01。
与0相异或,保留原值。
找出数组中单个数:自反性:x^ x=0,x^ 0=x 例如:数组[A B B ],A^B ^ B = A。
无需辅助变量交换二个数:a =a ^ b; b = a ^ b; a = a ^ b;
两数求和:无进位和 与 异或运算 规律相同,进位 和 与运算 规律相同
(5)按位取反
1变为0,0变为1;
(6)按位左移
二进制位左移若干位,移动的位数由右操作数指定(非负),空出的位用0填补,溢出则舍弃该高位。
左移1位相当于该数乘以2(左移时被溢出舍弃的高位中不包含1的情况)。
(7)按位右移
各二进制位右移若干位,移动的位数由右操作数指定(非负),移到右端的低位被舍弃。对于无符号数,高位补0。对于有符号数,某些机器将对左边空出的部分用
符号位
(0为正数,1为负数)填补(即“算术移位”),而另一些机器则对左边空出的部分用0填补(即“逻辑移位”)。
应用例子:
俄罗斯农民加法:
A*B:快速乘(两数相乘取模,相乘超出范围时用)

int quickMulti(int A, int B) {
    int ans = 0;
    for ( ; B; B >>= 1) {
        if (B & 1) {
            ans += A;
        }
        A <<= 1;
    }
    return ans;
}

(两数相乘取模,相乘超出范围时用)

int multi_mod(int a,int b,int c) {      // (a*b)%c 
int ret=0;
while(b) {
    if(b&1) { ret += a; if(ret >= c) ret -= c; }
    a<<=1;  if(a>=c) a-=c;  b>>=1;
}
return ret;

}

2、位运算性质

异或性质:
交换律:a ^ b=b ^ a
结合律:(a ^ b) ^ c == a^ (b ^ c)
x ^ 0 = x​ , x ^ 1 = ~x,x^ x=0
与运算:x & 0 = 0 , x & 1 = x

3、位运算应用问题

按位与:
整数幂:判断一个数n ,是不是2的整数幂。

#include<bits/stdc++.h>
using namespace std;
int main()
{  int n;
   cin>>n;
   if(n&(n-1))cout<<"NO";
   else cout<<"Yes";
}

计算一个数的二进制中1的个数。

#include<bits/stdc++.h>
using namespace std;
int main()
{  	int n = 0,num;
	unsigned int flag = 1;
	cin>>num;
    while(flag)
    {	if(num & flag) n++;
      	flag = flag << 1;
    }
    cout<<n;
 }

1的个数:
一个整数减一,可以得到该整数的最右边的1变为0,这个1右边的0变为1。对这个整数和整数减一进行与运算,将该整数的最右边的1变为0,其余位保持不变。直到该整数变为0,进行的与运算的次数即为整数中1的个数。

#include<bits/stdc++.h>
using namespace std;
int main()
{  	int n = 0,num;
	unsigned int flag = 1;
    cin>>num;
    while(num)
    {	num = num & (num - 1);
	    n++;
    }
    cout<<n;
}

按位异或:
1、给出 n 个整数,n 为奇数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间复杂度、常数空间复杂度找出出现了奇数次的那个数。
【输入样例】
9
3 3 7 2 4 2 5 5 4
【输出样例】
7

#include<bits/stdc++.h>
using namespace std;
int main()
{   int i,n,m,a;
    cin>>n;
    cin>>a;
    for(int i = 2; i <= n; i++) 
    {cin>>m; a^=m;   }
    cout<<a<<endl;
}

2、1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现
一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空
间,能否设计一个算法实现?
解法一、显然已经有人提出了一个比较精彩的解法,将所有数加起来,减去1+2+…+1000的和。
这个算法已经足够完美了,相信出题者的标准答案也就是这个算法,唯一的问题是,如果数列过大,则可能会导致溢出。
解法二、异或就没有这个问题,并且性能更好。
将所有的数全部异或,得到的结果与1231000的结果进行异或,得到的结果就是重复数。

#include<bits/stdc++.h>
using namespace std;
int main()
{  int i,n,a[11]={1,2,5,3,4,5,6,7,8,9,10};
   n=a[0]^a[1];
  for(i=2;i<=10;i++)n=n^a[i]; 
  for(i=1;i<=10;i++)n=n^i;
  cout<<n;
}

3、一系列数中,除两个数外其他数字都出现过两次,求这两个数字,并且按照从小到大的顺序输出.例如 2 2 1 1 3 4.最后输出的就是3 和4。

#include<bits/stdc++.h>
using namespace std;
int a[1000];
int main()
{       int n;
        scanf("%d", &n);
        int x = 0;
        for(int i = 1; i <= n; i++) {      scanf("%d", &a[i]); x ^= a[i];    }
        int num1 = 0, num2 = 0;
        int tmp = 1;
        while(!(tmp & x)) tmp <<= 1;
        cout<<tmp<<endl;
        for(int i = 1; i <= n; i++) {
             if(tmp & a[i]) num1 ^= a[i];
             else num2 ^= a[i];
            }
        printf("%d %d\n", min(num1, num2), max(num1, num2));
        return 0;

}

快速幂:力扣50. Pow(x, n)

参考链接:https://blog.csdn.net/weixin_43736974/article/details/84543970
内容来源于网络和链接整理,侵权联系删~

猜你喜欢

转载自blog.csdn.net/qq_41667348/article/details/124290718
今日推荐