这是我参与更文挑战的第17天,活动详情查看: 更文挑战
最近想把自己刷算法题的经验心得整理一下,一方面为了复习巩固,另一方面也希望我的分享能够帮助到更多在学习算法的朋友。
专栏名称叫《算法锦囊》,在讲解算法时会注重整体性,但不会面面俱到,适合有一定算法经验的人阅读。
这一次我们重点来看位运算,这一部分的所有题目和源码都上传到了github的该目录下,题解主要用Python语言实现。
概述
很多题目如果能用位运算解答会很巧妙,位运算是技巧性非常高的解法。
我整理了一些位运算当中常用到的知识点。
- 判断奇偶:
x%2==1 —>(x&1)==1 x%2==0 —>(x&1)==0 x>>1等价于x/2X=X&(X-1)清零最低位的1X&-X得到最低位的 1X&~X=>0
191. 位1的个数
题目地址为 191. 位1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
思路1,直接获取二级制字符串并进行统计。
class Solution:
def hammingWeight(self, n: int) -> int:
return bin(n).count("1")
思路2,巧妙利用位运算,效率更高。
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
while n > 0:
n = n & (n-1)
count += 1
return count
231. 2的幂
题目地址为 231. 2的幂
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。
示例 1:
输入:n = 1
输出:true
解释:20 = 1
如果用传统方法,则需要进行迭代。
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
if n == 0:
return False
while n % 2 == 0:
n //= 2
return n == 1
也可以利用位运算的特性,如果是2的幂,那么和比它小1的数的与为0。
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
if n == 0: return False
return n & (n-1) == 0
338. 比特位计数
题目地址为 338. 比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
传统解法就是每个数都去统计1的个数。
class Solution:
def countBits(self, num: int) -> List[int]:
def countOnes(x: int) -> int:
ones = 0
while x > 0:
x &= (x - 1)
ones += 1
return ones
bits = [countOnes(i) for i in range(num + 1)]
return bits
这题也可以利用动态规划来做,不过比较难想。
还记得我们可以利用 X&(X-1)来清除末尾的1吗,我们可以找到递归关系式:
bits[x]=bits[x&(x−1)] + 1
class Solution:
def countBits(self, num: int) -> List[int]:
bits = [0]
for i in range(1, num + 1):
bits.append(bits[i&(i-1)] + 1)
return bits
136. 只出现一次的数字
题目地址为 136. 只出现一次的数字,是一道面试高频题
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
这一题比较直观的做法是,借助于列表或者哈希表来辅助计算,但这样引入了额外的空间。
我们可以通过位运算的思路来巧妙地解答本题,两个相同的数的异或结果为0。
class Solution:
def singleNumber(self, nums: List[int]) -> int:
for i in range(1, len(nums)):
nums[i] ^= nums[i-1]
return nums[-1]
还可以利用Python里的reduce高阶函数,一行完成。
from functools import reduce
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return reduce(lambda x, y: x ^ y, nums)
168. Excel表列名称
最后,我们来看 168. Excel表列名称,这道题严格来讲不属于位运算,但我们可以从中窥探出进制转换的技巧。
给定一个正整数,返回它在 Excel 表中相对应的列名称。
例如,
1 -> A
2 -> B
3 -> C
...
26 -> Z
27 -> AA
28 -> AB
...
示例 1:
输入: 1
输出: "A"
这道题需要对被除数进行-1的处理,才能得到对应的字符。
class Solution:
def convertToTitle(self, n: int) -> str:
s = ''
while n:
n -= 1
s = chr(65 + n % 26) + s
n //= 26
return s
如果反过来从列名称反推序号,可看171. Excel表列序号。
class Solution:
def titleToNumber(self, columnTitle: str) -> int:
return sum([(ord(columnTitle[i]) - ord('A') + 1)*26**(len(columnTitle)-i-1) for i in range(len(columnTitle))])