位运算
异或运算
-
异或运算就是无进位相加
-
异或运算满足交换律、结合律
-
0^n = n,n^n = 0
-
整体异或和为 x,整体中某个部分的异或和为 y,那么剩下的部分的异或和为 x^y。即若 a^b = c,则 a = c^b,b = c^a
交换两个数
#include <iostream> #include <vector> using namespace std; // i == j 时会出错 void mySwap(vector<int> &arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; } int main() { // 两个变量必须有独立的内存空间 int a = 211; int b = -985; a = a ^ b; b = a ^ b; a = a ^ b; cout << a << " " << b << endl; vector<int> arr{1, 2, 3}; mySwap(arr, 0, 1); cout << arr[0] << arr[1] << arr[2] << endl; // 同一个内存空间,会变成 0 mySwap(arr, 2, 2); cout << arr[0] << arr[1] << arr[2] << endl; }
返回最大值
package org.example; public class Main { // 必须保证 n 一定是 0 或者 1 // 0 变 1,1 变 0 public static int flip(int n) { return n ^ 1; } // 非负数返回 1 // 负数返回 0 public static int sign(int n) { // 无符号右移 return flip(n >>> 31); } // 有溢出风险的实现 public static int getMax1(int a, int b) { int c = a - b; // c非负,returnA -> 1 // c非负,returnB -> 0 // c负数,returnA -> 0 // c负数,returnB -> 1 int returnA = sign(c); int returnB = flip(returnA); return a * returnA + b * returnB; } // 没有任何问题的实现 public static int getMax2(int a, int b) { // c 可能是溢出的 int c = a - b; // a 的符号 int sa = sign(a); // b 的符号 int sb = sign(b); // c 的符号 int sc = sign(c); // 判断 A 和 B,符号是不是不一样,如果不一样 diffAB = 1,如果一样 diffAB = 0 int diffAB = sa ^ sb; // 判断 A 和 B,符号是不是一样,如果一样 sameAB = 1,如果不一样 sameAB = 0 int sameAB = flip(diffAB); int returnA = diffAB * sa + sameAB * sc; int returnB = flip(returnA); return a * returnA + b * returnB; } public static void main(String[] args) { int a = Integer.MIN_VALUE; int b = Integer.MAX_VALUE; // getMax1 方法会错误,因为溢出 System.out.println(getMax1(a, b)); // getMax2 方法永远正确 System.out.println(getMax2(a, b)); } }
136. 只出现一次的数字
#include <vector> using namespace std; class Solution { public: int singleNumber(vector<int> &nums) { int XOR = 0; for (const auto &item: nums) XOR ^= item; // 最终出现在异或运算中两次的数字会抵消变成 0,最后单独出现的那个就是丢失的,因为没人和他抵消 return XOR; } };
268. 丢失的数字
#include <vector> using namespace std; class Solution { public: int missingNumber(vector<int> &nums) { int n = nums.size(); int XOR = 0; for (int i = 0; i < n; ++i) { // 这句保证了[0,n]中除了丢失的那个数全都参与异或运算 XOR ^= nums[i]; // 这句和结尾的 return 保证了[0,n]全都参与异或运算 XOR ^= i; } return XOR ^ n; // 最终出现在异或运算中两次的数字会抵消变成 0,最后单独出现的那个就是丢失的,因为没人和他抵消 } };
260. 只出现一次的数字 III
- Brian Kernighan 算法:
n & (~n + 1)
提取出整数n最后一位为1的数,等价于n & (-n)
#include <iostream> #include <vector> using namespace std; class Solution { public: vector<int> singleNumber(vector<int> &nums) { int xorSum = 0; for (const auto &num: nums) xorSum ^= num; // 此时 xorSum == a ^ b,xorSum 的二进制中,在 k 位置为 1,说明在 a 和 b 中的 k 位置是相反的 // 防止溢出 int rightOne = (xorSum == INT_MIN) ? xorSum : (xorSum & (~xorSum + 1)); // int rightOne = (xorSum == INT_MIN) ? xorSum : (xorSum & (-xorSum)); int xorSum2 = 0; for (const auto &num: nums) { // 只异或上 k 位置为 0 的数,最终结果就是 a 和 b 中的一个 if ((num & rightOne) == 0) xorSum2 ^= num; } return vector<int>{xorSum2, xorSum ^ xorSum2}; } };
137. 只出现一次的数字 II
#include <iostream> #include <vector> using namespace std; class Solution { public: // 已知数组中只有 1 种数出现次数少于 m 次,其他数都出现了 m 次 // 返回出现次数小于 m 次的那种数 int find(vector<int> &nums, int m) { vector<int> counts(32, 0); // 统计每一位上 1 出现的次数 for (const auto &num: nums) for (int i = 0; i < 32; ++i) counts[i] += (num >> i) & 1; int res = 0; for (int i = 0; i < 32; ++i) // 查看余数不是 0 的位置 if (counts[i] % m != 0) res |= (1 << i); return res; } int singleNumber(vector<int> &nums) { return find(nums, 3); } };
本文作者:n1ce2cv
本文链接:https://www.cnblogs.com/sprinining/p/18424142
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步