前言
这是我参与新手入门的第2篇文章。
电脑执行位运算效率高,平时代码写位运算可以装b,比如i除以2可以写i>>1;判断i是否是奇数可以用if(i&1),同理计算i%4可以直接写i&3.本文分享leetcode中关于位运算数学题。
目录
题1,阶乘
leetcode 50.pow(x,n) 点击展开
题目:实现 pow(x, n) ,即计算 x 的 n 次幂函数。
思路:分治法,总共要实现n个x相乘。将转化二进制数。比如77 的二进制表示 1001101,对应着77=1+4+8+64.即20,22,23,26.于是有,x77=x * x4 * x8 * x64.时间复杂度logN。
代码:
class Solution:
def myPow(self, x: float, n: int) -> float:
m = -n if n < 0 else n
y = 1
while m:
if m & 1:
y *= x
x *= x
m >>= 1
return y if n >= 0 else 1/y
题2,二进制加法
leetcode67.二进制求和 点击展开
题目:给你两个二进制字符串,返回它们的和(用二进制表示)。 输入为 非空 字符串且只包含数字 1 和 0。
解法与思路:1遍历字符串,从个位开始相加,考虑进位;
2内置函数直接return '{:b}'.format(int(a, 2) + int(b, 2));
3利用位运算,不用加减乘除。利用异或和与,实现真正的二进制加法。
解法三代码:
//先不考虑进位,0位与1位相加等于1,1位与1位相加和0位与0位相加均为1,对应异或运算
//两个位均为1时需要进位,对应与预算
class Solution:
def addBinary(self, a, b) -> str:
x, y = int(a, 2), int(b, 2)
while y:
answer = x ^ y # 第一步不考虑进位情况下两数相加的结果
carry = (x & y) << 1 # 需要进位的地方,左移一位正好等于进位
x, y = answer, carry # 重复,第一步的结果加上进位的值
return bin(x)[2:]
题3,格雷编码
89.格雷编码
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。即使有多个不同答案,你也只需要返回其中一种。
示例
输入: 2
输出: [0,1,3,2]
解释:
00 - 0
01 - 1
11 - 3
10 - 2
对于给定的 n,其格雷编码序列并不唯一。 例如,[0,2,3,1] 也是一个有效的格雷编码序列。
//法一 循环,每一轮在各数的左边加位1
class Solution:
def grayCode(self, n: int) -> List[int]:
res=[0]
for i in range(n):
offset = 1 << i
res+=[offset+x for x in res[::-1]]
return res
法二,递归,找到规律,每次改变x的一个位置
var grayCode = function(n) {
var x = 0;
var res=[];
core(n, x);
return res;
function core(n){
res.push(x);
for(let i=1; i<=n; i++){
x^=(1<<(i-1));
core(i-1, x);
}
}
};
//法三数学公式生成
var grayCode = function(n) {
var ans = new Array();
for(let i=0;i<(1<<n);i++){
ans.push(i^(i>>1));
}
return ans;
};
题4,5,7,寻找 出现次数1次的数字
136,137,260只出现一次的数字
题136:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
分析:要求不使用额外空间,则不能存储已出现过的数,还有线性复杂度限制。只能使用数学方法,异或操作,相同的位置会抵消为0,最后的数字便是只出现一次的那个。
代码:
class Solution:
def singleNumber(self, nums: List[int]) -> int:
res = 0
for i in nums:
res ^= i
return res
题137:其他数字出现3次,找到出现一次的数
分析:与上题类似,需要设计一个方法,让出现3次的位变成0。需要2个数来记录,过程见代码
代码:
class Solution:
def singleNumber(self, nums: List[int]) -> int:
ones = twos = 0; # ones数字的每一位为1代表该位上出现1次,twos代表2次,
for i in nums: # 每一个位不可同在ones和twos中为1,要么出现1次要么2次
ones = (ones ^ i) & ~twos;
twos = (twos ^ i) & ~ones;
return ones
public int singleNumber(int[] nums) {
int one = 0, two = 0, three;
for (int num : nums) {
// two的相应的位等于1,表示该位出现2次
two |= (one & num);
// one的相应的位等于1,表示该位出现1次
one ^= num;
// three的相应的位等于1,表示该位出现3次
three = (one & two);
// 如果相应的位出现3次,则该位重置为0
two &= ~three;
one &= ~three;
}
return one;
}
题260:有2个数出现一次,其他数出现2次
分析:也要求线性复杂度,同理使用异或运算,会等到要求的2数a 和b的异或结果num。对于num中的位1,要么来自与a,要么来自与b中。使用num&(-num)找到最后num中最后一个位1,例如num=0b00110100,负数补码表示,为正数的反码加1,有-num = 0b11001100,num&(-num) = 0b100. 于是再次遍历数组,将数中左起第三位为1的数相异或,便能等到a或b,另一个数则通过异或num得知。
代码:
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
num=0
for x in nums:
num^=x
dif = num&(-num) //找到最左边的位1
res=0
for y in nums:
if dif&y:
res^=y
return [res,num^res]
小结
其实很多题都是数学题,位运算就是很神奇,计算机作位运算效率很高。
题解代码并非原创,作者收集整理罢了。还有部分内容没写,暂时分享到这。