LeetCode——位运算

67.二进制求和

方法

  1. 模拟。具体而言,该题目类似于大数加法。
  2. 位运算。我不怎么会位运算,所以在这里记录一下。

89.格雷编码

方法:

  1. 格雷码公式: g ( i ) = b ( i + 1 ) ⊕ b ( i ) g(i) = b(i + 1) \oplus b(i) g(i)=b(i+1)b(i)> 这个公式的意思是:第b个格雷码g的二进制表示的第i位等于b的二进制表示的第i+1位和第i位异或。
    比如4位格雷码: 第5个格雷码g = 0111,它的计算过程如下所示: 已知5的二进制表示是0101,那么根据格雷码公式: g ( 3 ) = 0 , g ( 2 ) = b ( 3 ) ⊕ b ( 2 ) , g ( 1 ) = b ( 2 ) ⊕ b ( 1 ) , g ( 0 ) = b ( 1 ) ⊕ b ( 0 ) g(3) = 0,g(2) = b(3) \oplus b(2),g(1) = b(2) \oplus b(1),g(0) = b(1) \oplus b(0) g(3)=0,g(2)=b(3)b(2),g(1)=b(2)b(1),g(0)=b(1)b(0)。 写成竖式的形式:
    0 1 0 1
         \;\; 0 1 0 1(最后一位省略,高位补零,相当于右移一位)按位异或,得到
    0 1 1 1
  2. 对称法构造,详细说明见热评第一条

异或有以下三个性质:

  1. 任何数字和0异或,结果仍然是原来的数字,即 a ⊕ 0 = a a \oplus 0 = a a0=a
  2. 任何数字和其自身做异或运算,结果是0。即 a ⊕ a = 0 a \oplus a = 0 aa=0
  3. 异或运算符合交换律和结合律,即 a ⊕ b ⊕ a = b ⊕ a ⊕ a = b ⊕ ( a ⊕ a ) = b ⊕ 0 = b a \oplus b \oplus a = b \oplus a \oplus a = b \oplus (a \oplus a) = b \oplus 0 = b aba=baa=b(aa)=b0=b

位或|=
使用方式:a |= b
表达式含义:对ab执行按位或运算,并把结果分配给变量a


题目
136.只出现一次的数字

根据上述异或的性质,数组中全部元素异或的结果即为数组中只出现一次的数字。

190.颠倒二进制位

方法:按照由高位到低位计算无符号32整数的各个二进制位,同时计算颠倒后的结果。
这个方法是我能做到的极限。

191.位1的个数

这道题目没什么好说的,就是位运算符的简单应用。

201.数字范围按位与

与运算的规则如下:

  • 0 & 0 = 0
  • 0 & 1 = 0
  • 1 & 0 = 0
  • 1 & 1 = 1

对上述规则进行总结,不难得到:

  • 0 与 任何数(这里的任何数指的是0和1)结果都是0
  • 只有 1 与 1 的结果才是1

对于这道题目,最直观的做法就是遍历数字范围,依次执行按位与运算,但是数字范围是整个int类型的正数范围,直接遍历会超时。

注意到结果一定可以表示成 32 32 32位的整数,因此我们可以想方设法地计算结果的每一位。那么如何根据给定的数字范围判断第i位是 0 0 0还是 1 1 1

由与运算的规则,只要在数字范围内存在一个数字的第i位是 0 0 0,那么结果的第i位是 0 0 0那么问题就转换成如何判断在给定数字范围内是否存在一个数字的第i位是 0 0 0

具体做法如下:

  • 判断左端点和右端点的第i位是否为 0 0 0,若为0,则结果的第i位是 0 0 0
  • 或判断左端点和右端点右移i位后是否相同。若不同,则结果第i位是 0 0 0

第一个判断方法好理解,第二个判断方法为什么正确? 假设左端点left和右端点right第第i位是 1 1 1,且右移i位后不相同,那么right - left必然是一个偶数,这意味着在给定数字区间内第i位变化偶数次,根据与运算规则,结果的第i位必然是 0 0 0

371.两整数之和


方法:位运算。

既然题目不允许允许使用+,-等运算符,那么可以另辟蹊径,使用二进制运算符:与、或、非、异或以及同或等。此外,在涉及到加法时,进位是一定要考虑的。

两个数字均为int类型,二进制表示为32位。对于两个数字当前位xy,设进位为t:

  1. xy均为1,结果的当前位取决于x ^ y ^ t,进位一定是1。注意,^是异或运算符,异或又被称为不进位加法。
  2. xy其中有一个是1,结果的当前位取决于1 ^ t,进位t不变。
  3. xy都是0,则当前位是t,进位一定是0

这个过程类似于CMOS基础中的加法器。


393.UTF-8编码验证

这是一道简单的模拟题目,涉及到的知识点是位运算。

如何取一个整数integer的低 8 8 8位的二进制表示?
答:利用interger & 255,即可得到整数integer的低 8 8 8位的二进制表示。

如何取得一个字节数x的最高 n n n位?
答:x >> (8 - n)

由于题目限制给定整数的大小不超过 255 255 255,因此所给整数的高24位全为0,没有必要和255相与提取低8位。

通过上述分析,提出如下算法:

  1. 遍历数组,设当前遍历到第 i i i个元素
  2. (data[i] >> 7 & 1) == 0,说明是1字节字符
  3. (data[i] >> 5) == 6,说明是2字节字符,继续判断后面相邻数字是否满足i < data.size() && (data[i + 1] >> 6) == 2
  4. (data[i] >> 4) == 14,说明是3字节字符,继续判断后面相邻2个数字是否满足i + 2 < data.size() && (data[i + 1] >> 6) == 2 && (data[i + 2] >> 6) == 2
  5. (data[i] >> 4) == 30,说明是4字节字符,继续判断后面相邻3个数字是否满足i + 3 < data.size() && (data[i + 1] >> 6) == 2 && (data[i + 2] >> 6) == 2 && (data[i + 3] >> 6) == 2
  6. 若以上情况都不满足,直接返回false

401.二进制手表

初看题目以为回溯可以解决,想了一会发现不知道怎么回溯,随后灵机一动,可以枚举小时和分钟来解决这个问题。完成之后看题解,发现枚举可以解决。废话不多说了,介绍枚举方法吧。

思路和算法

我的思路是这样的:

  1. 题目中给的信息只有当前亮着的 LED 的数量,并没有告诉我们顶部的LED亮着的数目和底部LED灯亮着的数目。因此,需要对顶部的LED亮着的数目进行枚举。
  2. 在知道顶部的LED亮着的数目的情况下,哪些灯亮着还是未知的。因此需要枚举 [ 0 , 11 ] [0,11] [0,11]中的数字,计算其二进制表示中1的个数,判断其是否是合法的。
  3. 分钟的计算同小时的计算。

根据上述思路,所设计的算法步骤如下:

  1. 枚举顶部LED亮着的数目
  2. 枚举 [ 0 , 11 ] [0,11] [0,11]中的数字,寻找所有合法的数字hour
  3. 枚举 [ 0 , 59 ] [0,59] [059]中的数字,寻找所有合法的数字minute
  4. hourminute转换为字符串,按照时间的表示把字符串拼接起来,最后加到答案中

405.数字转换为十六进制数

  1. 负数在计算机中是以补码的形式存储的,在转换为16进制数字时不需要考虑把负数的二进制表示转换为补码。
  2. 进制转换方法:先转2进制,再转16进制

477. 汉明距离总和

思路1 : 双重循环暴力求解,超时。
思路2 : 统计数组nums中所有数字的二进制表示中第i位的0的个数cnt_zero1的个数cnt_one,那么数组nums中所有数字在第i位的汉明距离可以表示为cnt_one * cnt_zero


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值