@@ -32,160 +32,163 @@ diff=(n&(n-1))^n
32
32
33
33
> 给定一个** 非空** 整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
34
34
35
- ``` go
36
- func singleNumber (nums []int ) int {
37
- // 10 ^10 == 00
38
- // 两个数异或就变成0
39
- result := 0
40
- for i := 0 ;i<len (nums);i++{
41
- result=result^nums[i]
42
- }
43
- return result
44
- }
35
+ ``` Python
36
+ class Solution :
37
+ def singleNumber (self , nums : List[int ]) -> int :
38
+
39
+ out = 0
40
+ for num in nums:
41
+ out ^= num
42
+
43
+ return out
45
44
```
46
45
47
46
[ single-number-ii] ( https://leetcode-cn.com/problems/single-number-ii/ )
48
47
49
48
> 给定一个** 非空** 整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
50
49
51
- ``` go
52
- func singleNumber (nums []int ) int {
53
- // 统计每位1的个数
54
- var result int
55
- for i := 0 ; i < 64 ; i++ {
56
- sum := 0
57
- for j := 0 ; j < len (nums); j++ {
58
- // 统计1的个数
59
- sum += (nums[j] >> i) & 1
60
- }
61
- // 还原位00^10=10 或者用| 也可以
62
- result ^= (sum % 3 ) << i
63
- }
64
- return result
65
- }
50
+ ``` Python
51
+ class Solution :
52
+ def singleNumber (self , nums : List[int ]) -> int :
53
+ seen_once = seen_twice = 0
54
+
55
+ for num in nums:
56
+ seen_once = ~ seen_twice & (seen_once ^ num)
57
+ seen_twice = ~ seen_once & (seen_twice ^ num)
58
+
59
+ return seen_once
66
60
```
67
61
68
62
[ single-number-iii] ( https://leetcode-cn.com/problems/single-number-iii/ )
69
63
70
64
> 给定一个整数数组 ` nums ` ,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
71
65
72
- ``` go
73
- func singleNumber (nums []int ) []int {
74
- // a=a^b
75
- // b=a^b
76
- // a=a^b
77
- // 关键点怎么把a^b分成两部分,方案:可以通过diff最后一个1区分
78
-
79
- diff := 0
80
- for i := 0 ;i<len (nums);i++{
81
- diff^=nums[i]
82
- }
83
- result := []int {diff,diff}
84
- // 去掉末尾的1后异或diff就得到最后一个1的位置
85
- diff=(diff&(diff-1 ))^diff
86
- for i := 0 ;i<len (nums);i++{
87
- if diff&nums[i]==0 {
88
- result[0 ]^=nums[i]
89
- }else {
90
- result[1 ]^=nums[i]
91
- }
92
- }
93
- return result
94
- }
66
+ ``` Python
67
+ class Solution :
68
+ def singleNumber (self , nums : int ) -> List[int ]:
69
+ # difference between two numbers (x and y) which were seen only once
70
+ bitmask = 0
71
+ for num in nums:
72
+ bitmask ^= num
73
+
74
+ # rightmost 1-bit diff between x and y
75
+ diff = bitmask & (- bitmask)
76
+
77
+ x = 0
78
+ for num in nums:
79
+ # bitmask which will contain only x
80
+ if num & diff:
81
+ x ^= num
82
+
83
+ return [x, bitmask^ x]
95
84
```
96
85
97
86
[ number-of-1-bits] ( https://leetcode-cn.com/problems/number-of-1-bits/ )
98
87
99
88
> 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为[ 汉明重量] ( https://baike.baidu.com/item/%E6%B1%89%E6%98%8E%E9%87%8D%E9%87%8F ) )。
100
89
101
- ``` go
102
- func hammingWeight (num uint32 ) int {
103
- res := 0
104
- for num!=0 {
105
- num=num&(num-1 )
106
- res++
107
- }
108
- return res
109
- }
90
+ ``` Python
91
+ class Solution :
92
+ def hammingWeight (self , n : int ) -> int :
93
+ num_ones = 0
94
+ while n > 0 :
95
+ num_ones += 1
96
+ n &= n - 1
97
+ return num_ones
110
98
```
111
99
112
100
[ counting-bits] ( https://leetcode-cn.com/problems/counting-bits/ )
113
101
114
102
> 给定一个非负整数 ** num** 。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
115
103
116
- ``` go
117
- func countBits (num int ) []int {
118
- res := make ([]int ,num+1 )
119
-
120
- for i := 0 ;i<=num;i++{
121
- res[i]=count1 (i)
122
- }
123
- return res
124
- }
125
- func count1 (n int )(res int ){
126
- for n!=0 {
127
- n=n&(n-1 )
128
- res++
129
- }
130
- return
131
- }
104
+ 思路:利用上一题的解法容易想到 O(nk) 的解法,k 为位数。但是实际上可以利用动态规划将复杂度降到 O(n),想法其实也很简单,即当前数的 1 个数等于比它少一个 1 的数的结果加 1。下面给出三种 DP 解法
105
+
106
+ ``` Python
107
+ # x <- x // 2
108
+ class Solution :
109
+ def countBits (self , num : int ) -> List[int ]:
110
+
111
+ num_ones = [0 ] * (num + 1 )
112
+
113
+ for i in range (1 , num + 1 ):
114
+ num_ones[i] = num_ones[i >> 1 ] + (i & 1 ) # 注意位运算的优先级
115
+
116
+ return num_ones
117
+ ```
118
+
119
+ ``` Python
120
+ # x <- x minus right most 1
121
+ class Solution :
122
+ def countBits (self , num : int ) -> List[int ]:
123
+
124
+ num_ones = [0 ] * (num + 1 )
125
+
126
+ for i in range (1 , num + 1 ):
127
+ num_ones[i] = num_ones[i & (i - 1 )] + 1
128
+
129
+ return num_ones
132
130
```
133
131
134
- 另外一种动态规划解法
135
-
136
- ``` go
137
- func countBits (num int ) []int {
138
- res := make ([]int ,num+1 )
139
- for i := 1 ;i<=num;i++{
140
- // 上一个缺1的元素+1即可
141
- res[i]=res[i&(i-1 )]+1
142
- }
143
- return res
144
- }
132
+ ``` Python
133
+ # x <- x minus left most 1
134
+ class Solution :
135
+ def countBits (self , num : int ) -> List[int ]:
136
+
137
+ num_ones = [0 ] * (num + 1 )
138
+
139
+ left_most = 1
140
+
141
+ while left_most <= num:
142
+ for i in range (left_most):
143
+ if i + left_most > num:
144
+ break
145
+ num_ones[i + left_most] = num_ones[i] + 1
146
+ left_most <<= 1
147
+
148
+ return num_ones
145
149
```
146
150
147
151
[ reverse-bits] ( https://leetcode-cn.com/problems/reverse-bits/ )
148
152
149
153
> 颠倒给定的 32 位无符号整数的二进制位。
150
154
151
- 思路:依次颠倒即可
152
-
153
- ``` go
154
- func reverseBits (num uint32 ) uint32 {
155
- var res uint32
156
- var pow int =31
157
- for num!=0 {
158
- // 把最后一位取出来,左移之后累加到结果中
159
- res+=(num&1 )<<pow
160
- num>>=1
161
- pow--
162
- }
163
- return res
164
- }
155
+ 思路:简单想法依次颠倒即可。更高级的想法是考虑到处理超长比特串时可能出现重复的pattern,此时如果使用 cache 记录出现过的 pattern 并在重复出现时直接调用结果可以节约时间复杂度,具体可以参考 leetcode 给出的解法。
156
+
157
+ ``` Python
158
+ import functools
159
+
160
+ class Solution :
161
+ def reverseBits (self , n ):
162
+ ret, power = 0 , 24
163
+ while n:
164
+ ret += self .reverseByte(n & 0x ff ) << power
165
+ n = n >> 8
166
+ power -= 8
167
+ return ret
168
+
169
+ # memoization with decorator
170
+ @functools.lru_cache (maxsize = 256 )
171
+ def reverseByte (self , byte ):
172
+ return (byte * 0x 0202020202 & 0x 010884422010 ) % 1023
165
173
```
166
174
167
175
[ bitwise-and-of-numbers-range] ( https://leetcode-cn.com/problems/bitwise-and-of-numbers-range/ )
168
176
169
177
> 给定范围 [ m, n] ,其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。
170
178
171
- ``` go
172
- func rangeBitwiseAnd (m int , n int ) int {
173
- // m 5 1 0 1
174
- // 6 1 1 0
175
- // n 7 1 1 1
176
- // 把可能包含0的全部右移变成
177
- // m 5 1 0 0
178
- // 6 1 0 0
179
- // n 7 1 0 0
180
- // 所以最后结果就是m<<count
181
- var count int
182
- for m!=n{
183
- m>>=1
184
- n>>=1
185
- count++
186
- }
187
- return m<<count
188
- }
179
+ 思路:直接从 m 到 n 遍历一遍显然不是最优。一个性质,如果 m 不等于 n,则结果第一位一定是 0 (中间必定包含一个偶数)。利用这个性质,类似的将 m 和 n 右移后我们也可以判断第三位、第四位等等,免去了遍历的时间复杂度。
180
+
181
+ ``` Python
182
+ class Solution :
183
+ def rangeBitwiseAnd (self , m : int , n : int ) -> int :
184
+
185
+ shift = 0
186
+ while m < n:
187
+ shift += 1
188
+ m >>= 1
189
+ n >>= 1
190
+
191
+ return m << shift
189
192
```
190
193
191
194
## 练习
0 commit comments