异或运算:相同为0,不同为1,可以理解为无进位相加,且结果与参与运算的数顺序无关。在C++程序中,异或运算的符号为^。
两个重要的性质:
a^a = 0;
a^0 = a;
以下为一些应用到异或运算的题目:
1.如何不使用额外变量交换两个数的值?
常规交换代码如下:(通过申请一个临时变量来保存其中一个值)
void Swap01(int& val1,int& val2)
{
int temp = val1;
val1 = val2;
val2 = temp;
}
因为要求不使用额外变量,因此可以选择异或运算来实现:
void Swap02(int& val1,int& val2)
{
val1 = val1 ^ val2; // val^val2
val2 = val1 ^ val2; // val1^val2^val2 = val1
val1 = val1 ^ val2; // val1^val2^val1 = val2
}
实现细节:由于形参无法改变实参的值,传参时必须使用引用&。
2. 一个数组中,只有一种数出现了奇数次,其余数均出现了偶数次,如何找到出现奇数次的数?
由于只有一种数出现了奇数次,因此对数组中所有数执行异或运算,由于a^a=0,因此异或结果即为出现奇数次的数。
int FindNum(vector<int>& v)
{
int ans = 0;
for (int i = 0; i < v.size(); i++)
{
ans ^= v[i];
}
return ans;
}
假设数组 v={1,2,3,4,4,3,2,4,1},实现结果如下:
3. 一个数组中,有两种数(假设是a和b)出现了奇数次,其余均出现了偶数次,如何找到出现奇数次的两种数?
关键是区分出现奇数次的两种数,代码如下:
void FindOdd2(vector<int>& v)
{
int ans = 0;
for (int i = 0; i < v.size(); i++)
{
ans ^= v[i];
}
// 寻找能区分两数的方法---最右侧不为1的位
int rightOne = ans & ((~ans) + 1); // 按位操作
int ans1 = 0;
for (int i = 0; i < v.size(); i++)
{
if ((v[i] & rightOne) != 0) // 对应位为1
{
ans1 ^= v[i];
}
}
cout << "其中一个出现奇数次的数为:" << ans1 << endl;
int ans2 = ans1 ^ ans;
cout << "另一个出现奇数次的数为:" << ans2 << endl;
}
对于数组中所有数的异或结果ans,假设其二进制中某一位(假设第i位)为1,即说明a和b的第i位不相同,因此可以利用该位来区分a和b。代码中采用的是求最右侧的1,即:
假设数组 v = {1,2,3,4,4,3,2,4,1,5},运行结果如下所示:
4.判断一个数中二进制个数(leetcode剑指offer15.二进制中1的个数)
class Solution {
public:
int hammingWeight(uint32_t n) {
int count = 0;
while(n!=0)
{
// 寻找n中的最右侧的1
int rightOne = n&((~n)+1);
// 计数
count++;
// 抹去最右侧的1
n ^= rightOne;
}
return count;
}
};