位运算在算法中有时候能够起奇效
C语言位运算符列表
运算符 | 名称 | 描述 | 示例 |
---|---|---|---|
& | 按位与 | 两操作数对应位均为1时结果为1 | a & b |
` | ` | 按位或 | 两操作数对应位至少一个为1时结果为1 |
^ | 按位异或 | 两操作数对应位不同时结果为1 | a ^ b |
~ | 按位取反 | 反转操作数的所有位(单目运算) | ~a |
<< | 左移 | 将二进制位左移指定位数 | a << n |
>> | 右移 | 将二进制位右移指定位数 | a >> n |
乘 2
a * 2 == a << 1,正负通用,即左移一位,二进制全体位数左移一位,整数翻倍,与普通运算一样有可能超出整型边界。由于负数采用补码表示,左移高位符号位丢失,但是符号位后方同样是 1,因此正负通用。
除 2
a / 2 == a >> 1,即带符号右移一位,二进制全体位数右移一位,最高位补原有符号位,由于是整数除法,必定会出现无法整除的情况,此时正负数的结果是不同的:
正数:与普通除法一致,若无法整除,向下取整,如 11 / 2 == 11 >> 1 == 5。
负数:与普通除法不一致,若无法整除,向下取整,而普通除法是向上取整的
判断奇偶
a & 1,“and 1” 操作保留最后一位,由于奇数最后一位为 1,偶数最后一位为 0,因此:
a & 1 == 1,a 为奇数。
a & 1 != 1,a 为偶数
交换两个数
通过二进制交换两个数。
a ^= b;
b ^= a;
a ^= b;
一个数与两个相同的数进行异或运算,结果必定等于自己;因为异或的结果是保留对方与自身位值不同的地方,也就是两个二进制数有差异的位,再拿这些有差异的位去对方身上异或运算,那就是自己了
a ^= b 时,a 的值为 a ^ b;
b ^= a 等于 b = b ^ (a ^ b) 等于 a;
a ^= b 等于 a = (a ^ b) ^ a 等于 b;
注意!数组存在特殊情况,nums[i] 与 nums[i] 交换时 nums[i] 将变成 0。因为异或交换两个数的前提是两个数分开存储,如果对于同一数组元素进行这么操作,由于只有一个存储单元,自己与自己异或会清空所有的 1 位,将变为 0,原理等同于 a 与 a 使用异或交换。
举例:
异或找出重复的元素:1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次
思路
1^2^...^n^...^n^...^1000,无论这两个n出现在什么位置,都可以转换成为1^2^...^1000^(n^n)的形式。
其次,对于任何数x,都有x^x=0,x^0=x。
1^2^...^n^...^n^...^1000 = 1^2^...^1000^(n^n)= 1^2^...^1000^0 = 1^2^...^1000(即序列中除了n的所有数的异或)。
令,1^2^...^1000(序列中不包含n)的结果为T
则1^2^...^1000(序列中包含n)的结果就是T^n。
T^(T^n)=n。
所以,将1^2^3^...^1000的结果和数组中所有的数异或,得到的结果就是重复数。
找不同
给定两个字符串 s
和 t
,它们只包含小写字母。
字符串 t
由字符串 s
随机重排,然后在随机位置添加一个字母。
请找出在 t
中被添加的字母。
示例 1:
输入:s = "abcd", t = "abcde" 输出:"e" 解释:'e' 是那个被添加的字母。
示例 2:
输入:s = "", t = "y" 输出:"y"
首先分析题目找出只有一个数量的数据,这时候可以想到使用异或,但是异或需要通过两个相同数据来实现 a 异或 a = 0
所以要想把两个字符串中内容全部异或,最终结果返回的就是单独存在的字母
给定一个包含 [0, n]
中 n
个数的数组 nums
,找出 [0, n]
这个范围内没有出现在数组中的那个数。
示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3
,因为有 3 个数字,所以所有的数字都在范围 [0,3]
内。2 是丢失的数字,因为它没有出现在 nums
中。
示例 2:
输入:nums = [0,1]
输出:2
解释:n = 2
,因为有 2 个数字,所以所有的数字都在范围 [0,2]
内。2 是丢失的数字,因为它没有出现在 nums
中。
示例 3:
输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9
,因为有 9 个数字,所以所有的数字都在范围 [0,9]
内。8 是丢失的数字,因为它没有出现在 nums
中。
int missingNumber(int* nums, int numsSize) {
int n = numsSize;
int i = 0;
for(int j = 0; j <numsSize ;j++){
i = i ^ nums[j] ^ j;
}
i = i ^ n;
return i;
}
参考Leetcode链接:
https://leetcode.cn/circle/discuss/CWtfko/
https://leetcode.cn/circle/discuss/mDhWhf/