-
Notifications
You must be signed in to change notification settings - Fork 14k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
342 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
### 题目描述 | ||
|
||
两个整数之间的[汉明距离](https://baike.baidu.com/item/汉明距离)指的是这两个数字对应二进制位不同的位置的数目。 | ||
|
||
给出两个整数 `x` 和 `y`,计算它们之间的汉明距离。 | ||
|
||
示例 : | ||
|
||
``` | ||
输入: x = 1, y = 4 | ||
|
||
输出: 2 | ||
|
||
解释: | ||
1 (0 0 0 1) | ||
4 (0 1 0 0) | ||
↑ ↑ | ||
``` | ||
|
||
### 题目解析 | ||
|
||
首先通过 异或 操作找出两个数字对应位不同的位置,然后统计这些位置的个数。 | ||
|
||
统计解法借鉴Java中Integer.bitCount()方法源码来进行讲解,通过固定步数得到异或后1的个数。 | ||
|
||
第一步:将奇数位与偶数位相加,可以得出每两位1的个数,并将个数记录在这两位空间中 | ||
|
||
> i = i - (( i >>> 1 ) & 0x55555555 ) | ||
> | ||
> ``` | ||
> 0x55555555 => 01 01 01 01 ... 01 01 | ||
> i & 0x55555555 取出奇数位的1 | ||
> (i >>> 1) & 0x55555555 取出偶数位的1 | ||
> 比如,两位的情况下总共就四种情况:00 11 01 10 | ||
> 假设 i = 00 11 01 10 | ||
> i & 0x55555555 = 00 11 01 10 | ||
> 01 01 01 01 | ||
> ----------- | ||
> 00 01 01 00 | ||
> (i >>> 1) & 0x55555555 = 00 01 10 11 | ||
> 01 01 01 01 | ||
> ----------- | ||
> 00 01 00 01 | ||
> 将奇数位1的个数与偶数位的1求和: | ||
> 00 01 01 00 | ||
> 00 01 00 01 | ||
> ----------- | ||
> 00 10 01 01 | ||
> 结合原数字可以看出,00(00:没有1) 11(10:两个1) 01(01:1个1) 10(01:1个1) | ||
> | ||
> 每两位在通过加法统计时,总共如下四种情况[i & 01 + (i>>>1) & 01]: | ||
> 11: 01 + 01 = 10 = 2, 10: 00 + 01 = 01 = 1, 01: 01 + 00 = 01 = 1, 00: 00 + 00 = 00 = 0 | ||
> 每两位在通过减法统计时,总共如下四种情况[i - (i>>>1) & 01]: | ||
> 11: 11 - 01 = 10 = 2, 10: 10 - 01 = 01 = 1, 01: 01 - 00 = 01 = 1, 00: 00 + 00 = 00 = 0 | ||
> 可以发现结果是一样的,但是少了一次位运算! | ||
> | ||
> 在将每两位1的个数统计完之后,就可以开始两位两位、四位四位...相加求出1的总数 | ||
> ``` | ||
|
||
第二步:通过相邻两位1的个数相加,求出每四位包含1的个数,并将结果存储在所在的四位中 | ||
|
||
> i = ( i & 0x33333333 ) + (( i >>> 2 ) & 0x33333333 ) | ||
> | ||
> ``` | ||
> 0x55555555 => 0011 0011 0011 ... 0011 0011 | ||
> 继续上一步的结果向下进行:00 10 01 01 | ||
> i & 0x33333333 = 0010 0101 | ||
> 0011 0011 | ||
> --------- | ||
> 0010 0001 | ||
> (i >>> 2) & 0x33333333 = 0000 1001 | ||
> 0011 0011 | ||
> --------- | ||
> 0000 0001 | ||
> | ||
> 就和得出每四位所包含1的个数 | ||
> 0010 0001 | ||
> 0000 0001 | ||
> --------- | ||
> 0010 0010 | ||
> 结合原数字可以看出,0011(0010:有两个1) 0110(0010:有两个1) | ||
> ``` | ||
|
||
第三步:通过相邻四位1的个数相加,求出每八位包含1的个数,并将结果存储在所在的八位中 | ||
|
||
>i = ( i + ( i >>> 4 )) & 0x0f0f0f0f; | ||
> | ||
>``` | ||
>0x0f0f0f0f => 00001111 ... 00001111 | ||
>继续上一步的结果向下进行:0010 0010 | ||
>i & 0x0f0f0f0f = 00100010 | ||
> 00001111 | ||
> -------- | ||
> 00000010 | ||
>(i >>> 4) & 0x0f0f0f0f = 00000010 | ||
> 00001111 | ||
> -------- | ||
> 00000010 | ||
>就和得出每八位所包含1的个数 | ||
>00000010 | ||
>00000010 | ||
>-------- | ||
>00000100 | ||
>结合原数字可以看出,00110110(00000100:有四个1) | ||
> | ||
>源码中直接先将相邻四位进行相加,然后做了一次无用位清除 | ||
>``` | ||
|
||
第四步:通过相邻八位1的个数相加,求出每十六位包含1的个数,并将结果存储在所在的十六位中 | ||
|
||
> i = i + ( i >>> 8 ); | ||
> | ||
> ``` | ||
> 可以理解为( i & 0x0f0f0f0f ) + (( i >>> 8 ) & 0x0f0f0f0f ); | ||
> | ||
> 0x0f0f0f0f => 00000000111111110000000011111111 | ||
> ``` | ||
|
||
第五步:通过将int类型前十六位1的个数与后16位1的个数相加,求出int中所有1的个数 | ||
|
||
> i = i + ( i >>> 16 ); | ||
> | ||
> ``` | ||
> 可以理解为( i & 0x0000ffff ) + (( i >>> 8 ) & 0x0000ffff ); | ||
> | ||
> 0x0000ffff => 00000000000000001111111111111111 | ||
> ``` | ||
|
||
第六步:去除无用的位 | ||
|
||
> return i & 0x3f; | ||
> | ||
> ``` | ||
> int类型32位,即最多0x100000个1,除此之外左边的位都是无用的。 | ||
> 0x3f => 00111111 | ||
> ``` | ||
|
||
### 动画理解 | ||
|
||
![](../Animation/Animation.mp4) | ||
|
||
|
||
| ||
|
||
### 参考代码 | ||
|
||
```java | ||
class Solution { | ||
public int hammingDistance(int x, int y) { | ||
return Integer.bitCount(x ^ y); | ||
} | ||
} | ||
``` | ||
|
||
bitCount源码: | ||
|
||
```java | ||
public static int bitCount(int i) { | ||
i = i - ((i >>> 1) & 0x55555555); | ||
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); | ||
i = (i + (i >>> 4)) & 0x0f0f0f0f; | ||
i = i + (i >>> 8); | ||
i = i + (i >>> 16); | ||
return i & 0x3f; | ||
} | ||
``` | ||
|
||
### 复杂度分析 | ||
|
||
时间复杂度:O(1) | ||
|
||
空间复杂度:O(1) |
Binary file not shown.
102 changes: 102 additions & 0 deletions
102
0477-total-hamming-distance/Article/0477-total-hamming-distance.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
### 题目描述 | ||
|
||
两个整数的 [汉明距离](https://baike.baidu.com/item/汉明距离/475174?fr=aladdin) 指的是这两个数字的二进制数对应位不同的数量。 | ||
|
||
计算一个数组中,任意两个数之间汉明距离的总和。 | ||
|
||
示例 : | ||
|
||
``` | ||
输入: 4, 14, 2 | ||
|
||
输出: 6 | ||
|
||
解释: 在二进制表示中,4表示为0100,14表示为1110,2表示为0010。(这样表示是为了体现后四位之间关系) | ||
所以答案为: | ||
HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. | ||
``` | ||
|
||
**注意:** | ||
|
||
1. 数组中元素的范围为从 `0`到 `10^9`。 | ||
2. 数组的长度不超过 `10^4`。 | ||
|
||
### 题目解析 | ||
|
||
已示例为例,两两暴力计算的时间复杂度为o(n^2),实现上肯定是没有问题,但是当数据量大的时候性能堪忧。 | ||
|
||
我们先将数组与结果的数字二进制写出来 | ||
|
||
``` | ||
4 0 1 0 0 | ||
14 1 1 1 0 | ||
2 0 0 1 0 | ||
HammingDistance(4, 14) = 1 0 1 0 | ||
HammingDistance(4, 2) = 0 1 1 0 | ||
HammingDistance(14, 2) = 1 1 0 0 | ||
``` | ||
|
||
结合结果,从左往右按列观察这三个数字的二进制与运算结果的二进制可以发现一种关系: | ||
|
||
数字个数 Count = 3 | ||
|
||
第一列: 0 1 0 ==> 1 * (3 -1) = 2 = 1 0 1 | ||
|
||
> 本列只有1个1,说明在所有数字的第一位中,有(Count - 1)个数字的第一位与 **本数字** 不同,也就是求距离的时候结果为1, 即这一位产生1的个数为1 * (3 -1) | ||
|
||
第二列: 1 1 0 ==> 2 * (3 -2) = 2 = 0 1 1 | ||
|
||
> 本列有2个1,说明在所有数字的第二位中,有(Count - 2)个数字的第二位与这 **两个数字** 不同,即这一位产生1的个数为(Count - 2)+ (Count - 2)= 2 *(3 - 2) | ||
|
||
第三列同第二列 | ||
|
||
第四列: 0 0 0 ==> 0 * (3 -0) = 0 = 0 0 0 | ||
|
||
> 本列所有数字相同,求距离时也就不会产生1, 结果为0 | ||
> | ||
> 如果是 1 1 1也一样,3 * (3 - 3), 结果依旧为0 | ||
|
||
总结 :每一列求距离产生1的个数 = 本列 1 的个数 * (数字个数 – 本列1的个数)= 本列 1 的个数 * 本列 0 的个数 | ||
|
||
### 动画理解 | ||
|
||
![](../Animation/Animation.mp4) | ||
|
||
|
||
|
||
|
||
| ||
|
||
### 参考代码 | ||
|
||
```java | ||
class Solution { | ||
public int totalHammingDistance(int[] nums) { | ||
int len=nums.length; | ||
int[] bitCount = new int[32]; | ||
if(len <= 1){ | ||
return 0; | ||
} | ||
for(int numIndex = 0; numIndex < len; numIndex++){ | ||
for(int bitIndex = 0; bitIndex < 32; bitIndex++){ | ||
bitCount[bitIndex] += nums[numIndex] & 1; | ||
nums[numIndex] = nums[numIndex] >> 1; | ||
if(nums[numIndex] == 0){ | ||
break; | ||
} | ||
} | ||
} | ||
int oneCount = 0; | ||
for(int bitIndex = 0; bitIndex < 32; bitIndex++){ | ||
oneCount += bitCount[bitIndex] * (len - bitCount[bitIndex]); | ||
} | ||
return oneCount; | ||
} | ||
} | ||
``` | ||
|
||
### 复杂度分析 | ||
|
||
时间复杂度:时间复杂度:O(N log C) 其中 C是常数,表示数组中数可能的最大值。 | ||
|
||
空间复杂度:O(log C) |
Binary file added
BIN
+5.05 MB
1281- subtract-the-product-and-sum-of-digits-of-an-integer/Animation/Animation.mp4
Binary file not shown.
68 changes: 68 additions & 0 deletions
68
...n-integer/Article/1281- subtract-the-product-and-sum-of-digits-of-an-integer.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
### 题目描述 | ||
|
||
给你一个整数 `n`,请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。 | ||
|
||
示例 1: | ||
|
||
``` | ||
输入:n = 234 | ||
输出:15 | ||
解释: | ||
各位数之积 = 2 * 3 * 4 = 24 | ||
各位数之和 = 2 + 3 + 4 = 9 | ||
结果 = 24 - 9 = 15 | ||
``` | ||
|
||
示例 2: | ||
|
||
``` | ||
输入:n = 4421 | ||
输出:21 | ||
解释: | ||
各位数之积 = 4 * 4 * 2 * 1 = 32 | ||
各位数之和 = 4 + 4 + 2 + 1 = 11 | ||
结果 = 32 - 11 = 21 | ||
``` | ||
|
||
**提示:** | ||
|
||
``` | ||
1 <= n <= 10^5 | ||
``` | ||
|
||
### 题目解析 | ||
|
||
1、通过取模运算遍历数字每一位 | ||
|
||
2、通过两个变量在遍历过程中分别记录求和与求积 | ||
|
||
### 动画理解 | ||
|
||
![](../Animation/Animation.mp4) | ||
|
||
| ||
|
||
### 参考代码 | ||
|
||
```java | ||
class Solution { | ||
public int subtractProductAndSum(int n) { | ||
int addResult = 0, mulResult = 1; | ||
while (n > 0) { | ||
int num = n % 10; | ||
n /= 10; | ||
addResult += num; | ||
mulResult *= num; | ||
} | ||
return mulResult - addResult; | ||
} | ||
} | ||
``` | ||
|
||
|
||
|
||
### 复杂度分析 | ||
|
||
时间复杂度:O(logN) | ||
|
||
空间复杂度:O(1) |