2 异或位运算大厂必刷题
文章目录
- 如何不用额外变量交换两个数
- 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
- 怎么把一个int类型的数,提取出最右侧的1来
- 怎么把一个int类型的数,获取位数为1的数量
- 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
- 一个数组中有一种数出现K次,其他数都出现了M次
如何不用额外变量交换两个数
int a,b;
a=a^b;
b=a^b;
a=a^b;
注意事项:
- a和b必须是两块独立的内存.
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
题解
- 解法1: 哈希表做词频统计
- 解法2: 只用一个变量遍历一遍
-
- 用eor异或所有人, 把最后结果留在自己身上, 这么干完一遍之后,最后你返回eor的值就是那个出现了奇数次的数.
怎么把一个int类型的数,提取出最右侧的1来
题意:
题解:
- a&(~a + 1)
- 等同于
- a & (-a)
怎么把一个int类型的数,获取位数为1的数量
int a;
int nums=0;
while(a)
{
a=a&(a-1);
nums++;
}
一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
题解1:
最常见的解题步骤,面试要求的
void printOddTimesNums(vector<int>& v)
{
int eor=0;
for(auto e: v)eor^=e;
// 假设 a 和 b 是 两种奇数.
// 从 eor 里随便取一个一位置的数,我们统一取最右侧
// right = eor & (~eor+1)
int right = eor & (-eor);
//
// 共有a和b两类的数据 , 将两类数据区分
// 我们假设最右侧为1是 属于a类的数据
// 能够与right按位与不等与0 ,说明属于a类
int a = 0;
for(auto e : v)
{
if(e & right)
{
a^=e;
}
}
int b = eor ^ a;
cout<< "a="<<a<<endl;
cout<<"b="<<b<<endl;
}
题解2:
void test()
{
vector<int> v = {1,1,2,2,3,3,3,5,44,55,66,44,55,66,66,66};
int a =0,b=0;
int eor =0;
for(auto e:v)
{
eor^=e;
}
// eor = a^b
int tmp=eor;
int i=0;
int pre=0; // 求出a或者b的数
while(tmp)
{
pre=tmp;
tmp^=v[i];
i++;
}
cout<< "a="<<pre<<endl;
cout<<"b="<<(pre^eor)<<endl;
}
一个数组中有一种数出现K次,其他数都出现了M次
题意:一个数组中有一种数出现K次a元素,其他数都出现了M次,M> 1,K<M我到,出现了次的数,要求,额外空复杂度0(1),时间复杂度ON)。
题解:
- 开一个 32位int的数组 v
- if v[i] % m == 0 说明 二进制第i位没出现过a元素。
int onlyKTimes(vector<int> &v, int k, int m)
{
vector<int> t(32, 0);
// t[0] 0位置的1出现了几个
// t[i] i位置的1出现了几个
for (auto num : v)
{
for (int i = 0; i <= 31; i++)
{
if ((num >> i) & 1 == 1)
{
t[i]++;
}
}
}
int ans = 0;
for (int i = 0; i <= 31; i++)
{
if (t[i] % m != 0)
{
ans |= (1 << i);
}
}
return ans;
}
对数器验证算法:
int testonlyKTimes(vector<int> &v, int k, int m)
{
unordered_map<int, int> map;
for (auto num : v)
{
if (map.find(num) != map.end())
{
map[num]++;
}
else
{
map[num] = 1;
}
}
int ans = 0;
for (auto num : map)
{
if (num.second == k)
{
ans = num.first;
break;
}
}
return ans;
}
vector<int> randomArray(int kinds, int range,int k, int m)
{
int kNum = rand()% range + 1;
int times = k; // kNum 出现k次
int numkinds = rand() % kinds + 1;
// k*1 + (numkinds - 1) * m;
vector<int> v(k+ (numkinds-1)*m);
int index = 0 ;
for(;index<times;index++)
{
v[index] = kNum;
}
numkinds--;
unordered_set<int> set;
set.insert(kNum);
while(numkinds!=0)
{
int curNum = 0 ;
do{
curNum = rand() % range +1;
}while(set.find(curNum)!=set.end());
set.insert(curNum);
numkinds--;
for(int i=0;i<m;i++)
{
v[index++]=curNum;
}
}
// v 填好了
// 由于数据太整齐了,我们打乱数据
for(int i=0;i<v.size();i++)
{
int j = rand() % v.size() ; // 0 ~ size-1;
int tmp = v[i];
v[i]=v[j];
v[j]=tmp;
}
return std::move(v);
}
// 对数器
void test2()
{
srand((int)time(NULL));
int kinds = 100; // 元素种类 1~ 100
int range = 300; // 元素范围 1~300
int testTime = 1000; // 测试次数
int max = 9; // k,m 范围 1~9
cout << "测试开始\n";
while (testTime--)
{
int a = rand()%max + 1;
int b = rand()%max + 1;
int k = std::min(a,b);
int m = std::max(a,b);
if(k==m){
m++;
}
vector<int> v = randomArray(kinds, range, k, m);
int ans1 = onlyKTimes(v, k, m);
int ans2 = testonlyKTimes(v, k, m);
if (ans1 != ans2)
{
cout << "出错了\n";
}
}
cout << "测试结束\n";
}
int main()
{
test2();
}