二进制的三种表示形式:原码、反码、补码
二进制转化为十进制:从左起第一个数字开始 2的零次方+2的1次方......
原码:即二进制表示,最高位是符号位,0代表正数,1代表负数,在位运算中符号位也参与运算。
反码:正数的反码就是其原码本身,负数的反码是符号位不变,其余位取反,也就是0变1,1变0.
补码:正数的补码仍是原码,负数的补码是反码+1。计算机内部使用补码表示。
二进制的计算
按位非操作~
~1 = 0
~0 = 1
~把num的补码中0和1全部取反,有符号整数的符号位在~运算中同样会取反。
按位与操作&
全是1即为1,有一个0即为0。
1&1 = 1
1&0 = 0
0&1 = 0
0&0 = 0
按位或操作|
只要有两个对应位中有一个1时就为1。
1|1 = 1
1|0 = 1
0|1 = 1
0|0 = 0
按位异或操作
只有两个对应位不同时才为1。
异或操作满足交换律和结合律。
1^1 = 0
1^0 = 1
0^1 = 1
0^0 = 0
A^A = 0
A^0 = A
按位左移操作<<
num << i 将num的二进制表示左移动 i 位所得的值。即前面去掉i位,后面用0补位。
num左移动i位后,十进制num变成了num*2的i次方。
按位右移操作>>
num>>i将num的二进制表示向右移i位所得的值。即后面去掉i位,前面用0补位。
num右移i位后,十进制num变成了num//2的i次方。
案例
只出现一次的数字
数字之间的异或:0^A=A
class Solution:
def singleNumber(self, nums: List[int]) -> int:
result = 0
for i in nums:
result ^= i
return result
只出现一次的数字Ⅲ
思路:
1)先整体做一次异或得到一个数字A;
2)再想办法将两个不一样的数字从第一个数字中分离出来,即A&A得到其中一个数字B,B再分别与其它数字进行&的操作,此时能够将所有数字分成两组,每一组中都有存在一个只出现过一次的数字。
3)分别对两组数字进行异或操作。
class Solution:
def singleNumber(self,nums:List[int]) -> List[int]:
if len(nums)<2:
return []
different = 0
for num in nums:
different ^= num
different &= -1*different
num1 = 0
num2 = 0
for num in nums:
if (num & different ) == 0:
num1 ^= num
else:
num2 ^= num
return [num1,num2]
任务
2的幂
思路:枚举
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
return n > 0 and n & (n - 1) == 0
子集
java
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
for (int i = 0; i < (1 << nums.length); i++) {
List<Integer> sub = new ArrayList<Integer>();
for (int j = 0; j < nums.length; j++)
if (((i >> j) & 1) == 1) sub.add(nums[j]);
res.add(sub);
}
return res;
}
}
只出现一次的数字
思路:java
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for(int i = 0; i < 32; i++){
int count = 0;
for (int j = 0; j < nums.length; j++) {
//先将数右移,并求出最后一位为 1 的个数
if ((nums[j] >> i & 1) == 1) {
count++;
}
}
//找到某一位取余为 1 的数,并左移,为了将这一位循环结束后移至原位
if (count % 3 != 0) {
res = res | 1 << i;
}
}
return res;
}
}