2.8 位集

2.8 位集

1.位操作技巧

//以下的“第i位”均是以最右边为第0位算起
//获取x表示的二进制第i位
(x&(1<<i))>>i;
//将x的第i位设置为1
x=x|(1<<i)
//将x的第i位设置为0
x=x&(~(1<<i))
//将x的第i位取反
x=x^(1<<i)
//获取x的二进制表示中从最右侧的1开始的所有二进制位
x&(-x)

2.配合例题:

Single Number I

给定一个整数数组,其中除了一个元素只出现一次外,其他元素都出现两次,找出这个数字。

要求算法具有 \(Ο(n)\)的时间复杂度且只使用常数量的额外内存空间。

//由异或的相关知识可得
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+7;
int a[N];//如果只是单纯查找就连数组都不用开,直接边输入边异或即可
int main()
{
    int n;
    cin>>n;
    int ans=0;
    for(int i=1;i<=n;i++)cin>>a[i],ans^=a[i];
    cout<<ans<<endl;
}

Single Number II

给定一个整数数组,除了一个元素只出现一次外,其他元素都出现三次,找出这个数字。

要求算法具有\(Ο(n)\)的时间复杂度且只使用常数量的额外内存空间

/*题目中只要按每个数的bit位相加然后%3即可得到想要的数。我们使用两个变量a,b来模拟两个bit位从而保证能够逢3变0
00 0
01 1
10 2
->00
*/
//因此我们有如下代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+7;
int c[N];//同样的如果只是进行查询操作连数组都不用开
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>c[i];
    int a=0,b=0;
    for(int i=1;i<=n;i++)
    {
        b=(b^c[i])&~a;
        a=(a^c[i])&~b;
    }
    cout<<b<<endl;//可以手动模拟一下(如果连续三个,四个甚至更多一样让你找一个不同一样按照这个思路,只不过就变成数组而不是变量了)
    return 0;
}

Single Number III

给定一个整数数组,其中有两个元素只出现一次,其他元素都出现两次,找出只出现一次的两个数。

要求算法具有 \(Ο(n)\)的时间复杂度且只使用常数量的额外内存空间。

//本题和I非常相似,只不过变成了两个不相同的数,思路是一致的
/*
如何区别两个数不相同?首先我们按照I的思路将所有数组全部异或消去所有出现两次的数,得到一个异或值,这个值必定是两个不相同的数的异或值(x^0=x,x^x=0),接下来进行如下操作
例如两个不同的数是8和6
我们拆开其二进制位
8:1000
6:0110
异或后得到1110(这个例子举的不太好......)
这两个数必然存在二进制表示中至少一位不同,我们使用lowbit取得异或值最右侧不为0的位(因为提前进行异或,不同的数必然这一位不同)
再分为两组一组这位位为1另一组为0即可,然后可以进行再次异或消掉两组中重复的数就是答案
*/
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+7;
int c[i];//与前两个注释一致
int main()
{
    int a=0,b=0;
    int diff=0;
    for(int i=1;i<=n;i++)cin>>a[i],diff^=c[i];
    diff=diff&(-diff);//lowbit
    for(int i=1;i<=n;i++)
    {
        if(c[i]&diff==0)a^=c[i];
        else b^=c[i];
    }
    cout<<a<<" "<<b<<endl;
    return 0;
}
posted @ 2021-08-09 22:43  Black-OR-White  阅读(55)  评论(0)    收藏  举报