Unverified Commit 3a802a7c authored by chilimyan's avatar chilimyan Committed by GitHub
Browse files

Merge pull request #3 from MisterBooo/master

更新代码
parents 53cc7424 6a20215c
This image diff could not be displayed because it is too large. You can view the blob instead.
This image diff could not be displayed because it is too large. You can view the blob instead.
# LeetCode 第 110 号问题:平衡二叉树
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 110 号问题:平衡二叉树。
### 题目描述
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
> 一个二叉树*每个节点* 的左右两个子树的高度差的绝对值不超过1。
**示例 1:**
```
3
/ \
9 20
/ \
15 7
```
返回 `true`
**示例 2:**
给定二叉树 `[1,2,2,3,3,null,null,4,4]`
```
1
/ \
2 2
/ \
3 3
/ \
4 4
```
返回 `false`
### 题目解析 - 自顶向下
这道题可以算是递归的充分使用了, 每一个子树都是子问题.
根据题意, 直观的想法就是计算当前节点左右子树的高度差了, 具体算法流程如下:
*定义* 方法 `depth(root)` 计算 root 最大高度
- **终止条件:**`root` 为空,即越过叶子节点,则返回高度 0
- **返回值:** Max(左子树高度, 右子树高度 ) + 1
*定义* 方法 `isBalanced(root)` 判断树 `root` 是否平衡
- **特例处理:** 若树根节点 `root` 为空,则直接返回 true
- **返回值:** 所有子树都需要满足平衡树性质,因此以下三者使用与 逻辑与 连接
- `abs(depth(root.left) - depth(root.right)) < 2` :判断 **当前子树** 是否是平衡树
- `isBalanced(root.left)` : 先序遍历递归,判断 **当前子树的左子树** 是否是平衡树;
- `isBalanced(root.right)` : 先序遍历递归,判断 **当前子树的右子树** 是否是平衡树;
> 通过流程能发现, 暴力法虽然容易想到, 但是会产生大量冗余计算, 因此时间复杂度也就会高;
>
> 想避免这种情况, 移步向下看 自底向上 方法
### 动画描述
<img src="../Animation/Animation1.gif" alt="Animation1" style="zoom:150%;" />
### 参考代码
```javascript
/**
* JavaScript 描述
* 自顶向下递归
*/
function depth(root) {
if (root == null) {
return 0;
}
return Math.max(depth(root.left), depth(root.right)) + 1;
};
var isBalanced = function(root) {
if (root == null) {
return true;
}
return Math.abs(depth(root.left) - depth(root.right)) < 2 &&
isBalanced(root.left) &&
isBalanced(root.right);
};
```
### 复杂度分析
- 时间复杂度: **O(Nlog_2 N)**
最差情况下, isBalanced(root) 遍历树所有节点,占用 O(N)O(N) ;判断每个节点的最大高度 depth(root) 需要遍历 各子树的所有节点 ,子树的节点数的复杂度为 O(log_2 N)
- 空间复杂度: **O(N)**
最差情况下(树退化为链表时),系统递归需要使用 O(N) 的栈空间
### 题目解析 - 自底向上
**自顶向下** 计算 `depth` 存在大量冗余, 每次调用 `depth` 时,要同时计算其子树高度。
**自底向上** 计算每个子树的高度只会计算一次。先递归计算当前节点的子节点高度,然后再通过子节点高度判断当前节点是否平衡,从而消除冗余。
**自底向上****自顶向下** 的逻辑相反,首先判断子树是否平衡,然后比较子树高度判断父节点是否平衡。算法如下:
*定义* 方法 `recur(root):` : 判断子树是否平衡 | 返回当前节点高度
- **递归终止条件:**
- 当越过叶子节点时, 返回高度 0
- 当左(右)子树高度 `left== -1` 时,代表此子树的 **左(右)子树** 不是平衡树, 因此直接返回 `-1`
- **递归返回值:**
- 当节点 `root` 左 / 右子树的高度差 < 2:返回以节点 root 为根节点的子树的最大高度Max( left, right ) + 1
- 当节点 `root` 左 / 右子树的高度差 >= 2 :则返回 `-1` , 代表 **此子树不是平衡树**
*定义* 方法 `isBalanced(root)` : 判断当前树是否平衡
- **返回值:**`recur(root) != 1` , 则说明此树平衡, 返回 `true` , 否则返回 `false`
### 动画描述
<img src="../Animation/Animation2.gif" alt="Animation2" style="zoom:150%;" />
### 参考代码
```javascript
/**
* JavaScript 描述
* 自底向上递归
*/
function recur(root) {
if (root == null) {
return 0;
}
let leftHeight = recur(root.left);
if (leftHeight == -1) {
return -1;
}
let rightHeight = recur(root.right);
if (rightHeight == -1) {
return -1;
}
return Math.abs(leftHeight - rightHeight) < 2 ?
Math.max(leftHeight,rightHeight) + 1 : -1;
};
var isBalanced = function(root) {
return recur(root) != -1;
};
```
### 复杂度分析
- 时间复杂度 **O(N)**: N为树的节点数;最差情况下,需要递归遍历树的所有节点。
- 空间复杂度 **O(N)**: 最差情况下(树退化为链表时),系统递归需要使用 O(N) 的栈空间。
![](../../Pictures/qrcode.jpg)
\ No newline at end of file
## LeetCode第118号问题:杨辉三角
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 个人博客:www.zhangxiaoshuai.fun
**本题选自leetcode第118题,easy级别,目前通过率66.4%**
### 题目描述:
```
给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
示例:
输入: 5
输出:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]
```
### 题目分析:
初中时候学习的杨辉三角想不到又在这里出现了,题意很容易理解,每一行中的第一个数字和最后一个数字都是1,中间的数字都是通过上面相邻的两个数字相加得到。题目给我们一个杨辉三角的非负行数,然后我们生成对应的杨辉三角(集合)。
既然返回的是一个List<List<Integer>>,那么我们用一个大集合来放置每一行的数,每一行的数我们分别用一个小集合来存放,最后将每一个小集合添加进大集合中。
### gif动画演示:
官方中已经有做的非常好的gif图解,这里直接展示:
![](../Animation/resource.gif)
### 代码:
```java
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> triangle = new ArrayList<List<Integer>>();
//给定的numRows为0时直接返回空集合即可
if (numRows == 0) {
return triangle;
}
//因为杨辉三角的第一行总是1,所以先新建一个list,并将1加入该list中
triangle.add(new ArrayList<>());
triangle.get(0).add(1);
//从第二行开始,新建表示当前行的list,拿到当前行的前一行的list
for (int rowNum = 1; rowNum < numRows; rowNum++) {
List<Integer> row = new ArrayList<>();
List<Integer> prevRow = triangle.get(rowNum-1);
//一行中的第一个元素
row.add(1);
//针对每一行,都是上一行的相邻的两个元素相加得到两个1中间的数
for (int j = 1; j < rowNum; j++) {
row.add(prevRow.get(j-1) + prevRow.get(j));
}
//一行中的最后一个元素
row.add(1);
//最后将“整行添加到大集合中”
triangle.add(row);
}
return triangle;
}
```
This image diff could not be displayed because it is too large. You can view the blob instead.
# LeetCode 第 133 号问题:克隆图
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 133 号问题:克隆图。题目难度为 Medium,目前通过率为 54.8% 。
### 题目描述
给你无向连通图中一个节点的引用,请你返回该图的深拷贝(克隆)。图中的每个节点都包含它的值 val(int)和其邻居的列表(list[Node])。
**示例 1:**
```
输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
输出:[[2,4],[1,3],[2,4],[1,3]]
解释:
图中有 4 个节点。
节点 1 的值是 1,它有两个邻居:节点 2 和 4 。
节点 2 的值是 2,它有两个邻居:节点 1 和 3 。
节点 3 的值是 3,它有两个邻居:节点 2 和 4 。
节点 4 的值是 4,它有两个邻居:节点 1 和 3 。
```
**示例 2:**
```
输入:adjList = [[]]
输出:[[]]
解释:输入包含一个空列表。该图仅仅只有一个值为 1 的节点,它没有任何邻居。
```
**示例 3:**
```
输入:adjList = [[2],[1]]
输出:[[2],[1]]
```
### 题目解析
给你一个图,让你完整地拷贝一份。这道题目不难,但是在实际的工作项目中却时常遇到。这道题目有很多种解法,但是建议站在实际工作的角度去思考。
图是由一个个节点组成的,完整地拷贝一份图,那么就意味着我们要对每个节点进行拷贝,而且节点与节点的关系也要拷贝过来。做到这一点也不难,我们只需要遍历一遍图就可以了,这里的关键点在于题目强调是 **无向图**,也就是说我们可以从图上的任意点出发到达图上的所有节点。那么问题从而就转换到了如何遍历图,我们可以使用广度优先搜索,也可以使用深度优先搜索,从工作的角度出发,比较推荐广度优先搜索,因为理解容易,实现简单,而且不涉及栈溢出的问题,处理大规模数据比较安全。
<br>
### 动画演示
![](../Animation/133.gif)
<br>
### 复杂度分析
一般使用广度优先搜索遍历图,时间复杂度是 `O(n + m)`,其中这里的 n 表示的是图上的节点数,m 表示的图上的边的数量。从广度优先搜索的 **由点及面** 的性质,你不难理解这个结果。极端情况下,当这张图是一张全联通的图,时间复杂度就会是 `O(n^2)`,解释起来也很容易,因为你每访问完一个节点,下面都会去访问相邻的节点,一个节点和所有的节点相连,那么在一个节点上花费的时间就是 n,在 n 节点上花费的时间就是 n^2。因为我们使用了队列存放接下来需要遍历的节点,空间复杂度就是 `O(n)`
![](../../Pictures/qrcode.jpg)
\ No newline at end of file
# LeetCode 第 142 号问题:环形链表 II
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
今天分享的题目来源于 LeetCode 上第 142 号问题:环形链表II。题目难度为 Medium 。
### 题目描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 `null`
为了表示给定链表中的环,我们使用整数 `pos` 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 `pos``-1`,则在该链表中没有环。
**示例 1:**
```
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
```
![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/vweoq.png)
**示例 2:**
```
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
```
![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/kxbrz.png)
**示例 3:**
```
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
```
![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/w3vsg.png)
**进阶:**
你是否可以不用额外空间解决此题?
### 题目解析 - 哈希表
普通解法就是利用哈希表保存访问过的节点, 同时遍历过程中检查哈希表中是否已存在相同的节点
### 代码实现
```javascript
/**
* JavaScript 描述
* 哈希表方法
*/
var detectCycle = function(head) {
let res = [ ];
while (head !== null) {
if (res.includes(head)) {
return head;
}
res.push(head);
head = head.next;
}
return null;
};
```
### 复杂度分析
- 时间复杂度:**O(n)**
- 空间复杂度:**O(n)**
### 题目解析 - Floyd 算法
Floyd算法 可以达到常量空间解决此问题.
我在维基百科找到了这个算法描述, 在此引用一下.
**Floyd判圈算法**(**Floyd Cycle Detection Algorithm**),又称 **龟兔赛跑算法**(**Tortoise and Hare Algorithm**),是一个可以在[有限状态机](https://zh.wikipedia.org/wiki/有限状态机)[迭代函数](https://zh.wikipedia.org/wiki/迭代函数)或者[链表](https://zh.wikipedia.org/wiki/链表)上判断是否存在[](https://zh.wikipedia.org/wiki/環_(圖論)),求出该环的起点与长度的算法。
如果有限状态机、迭代函数或者链表存在环,那么一定存在一个起点可以到达某个环的某处 ( 这个起点也可以在某个环上 )。
初始状态下,假设已知某个起点节点为节点 *S*。现设两个指针 `t``h` ,将它们均指向 *S*
接着,同时让 `t``h` 往前推进,但是二者的速度不同:`t` 每前进 `1` 步, `h` 前进 `2` 步。只要二者都可以前进而且没有相遇,就如此保持二者的推进。当 `h` 无法前进,即到达某个没有后继的节点时,就可以确定从 *S* 出发不会遇到环。反之当 `t``h` 再次相遇时,就可以确定从 S 出发一定会进入某个环,设其为环 *C*
如果确定了存在某个环,就可以求此环的起点与长度。
上述算法刚判断出存在环 *C* 时,显然 t 和 `h` 位于同一节点,设其为节点 *M*。显然,仅需令 `h` 不动,而t不断推进,最终又会返回节点 *M*,统计这一次t推进的步数,显然这就是环 *C* 的长度。
为了求出环 *C* 的起点,只要令h仍均位于节点 *M* ,而令t返回起点节点 *S* ,此时h与t之间距为环 *C* 长度的整数倍。随后,同时让 `t``h` 往前推进,且保持二者的速度相同:`t` 每前进 `1` 步,`h` 前进 `1` 步。持续该过程直至 `t``h` 再一次相遇,设此次相遇时位于同一节点 *P*,则节点 *P* 即为从节点 *S* 出发所到达的环 *C* 的第一个节点,即环 *C* 的一个起点。
**看完之后是不是很多疑点, 觉得为什么会这样呢?**
下面用数学简单证明一下
假设 链表的节点数为 `num`, 从 head 到链表环入口节点数为 `m` (不包含入口节点), 环的节点数为 `n`, 链表环入口设点为 *P*
由此可得 `num = m + n`
假设 慢指针 `Tortoise` (乌龟) 每次走 `1` 个节点, 走了 `x`
假设 快指针 `Hare` (兔子) 每次走 `2` 个节点, 走了 `f`
那么 `f = 2x`
当第一次相遇时, 必然是在环内, 设其点为 *M*, 兔子第一次到达 *M* 点后至少又在环内饶了一圈后追上乌龟,
假设绕了 `k` 圈, 那么可以得到
`f = x + kn`
兔子到达 *P* 点的步数为
`f = m + kn`
`f = 2x``f = x + kn` 两个等式可以得到 `x = kn`
`f = m + kn``x = kn` 可知, 乌龟到达 *P* 点还需要走 `m`
`m` 的长度正是从 head 到链表环入口节点数的长度, 这是未知的,
那么让兔子从 head 以乌龟的速度走, 乌龟在 *M* 点走, 当兔子和乌龟相遇时即走了 `m` 步, 也就到达了 *P* 节点.
### 动画描述
![](../Animation/Animation.gif)
### 代码实现
```java
/**
* JavaScript 描述
* Floyd判圈算法
*/
var detectCycle = function(head) {
if (head == null) {
return head;
}
// 设置快慢指针
let tortoise = head,
hare = head;
// 检查链表是否有环
while (true) {
if (hare == null || hare.next == null) {
return null;
}
hare = hare.next.next;
tortoise = tortoise.next;
if (hare == tortoise) {
break;
}
}
// 兔子和乌龟第二次相遇找到环入口
hare = head;
while (hare != tortoise) {
hare = hare.next;
tortoise = tortoise.next;
}
return hare;
};
```
### 复杂度分析
- 时间复杂度:**O(n)**
- 有环情况下, 第一次和第二次相遇, 乌龟步数都小于链表节点数, 因此与链表节点数成线性关系;
- 无环情况下, 兔子大约需要 n/2 步数到达最后, 因此也与链表节点数成线性关系.
- 空间复杂度:**O(1)** , 双指针使用常数大小的额外空间
![](../../Pictures/qrcode.jpg)
\ No newline at end of file
## LeetCode第234号问题:回文链表
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 个人博客:www.zhangxiaoshuai.fun
**本题选择leetcode第234题,easy难度,目前通过率41.5%**
```txt
题目描述:
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
```
***这道题还有进阶版本,我们先实现这个普通版本再看。***
### 题目分析:
```
首先,我们先遍历一遍链表,将链表中的每个值存入数组当中,然后我们判断数组中的元素是否满足回文数条件即可。
这里因为我们不知道链表的长度,我们先使用动态数组将值存起来,然后再存到固定大小的数组中。
```
### 解法一gif动画演示:
![01](../Animation/solved01.gif)
### 代码:
```java
public boolean isPalindrome(ListNode head) {
List<Integer> list = new ArrayList<>();
while (head != null) {
list.add(head.val);
head = head.next;
}
int[] arr = new int[list.toArray().length];
int temp = 0;
for (int a : list) {
arr[temp++] = a;
}
temp = 0;
for (int i = 0;i < arr.length/2;i++) {
if (arr[i] == arr[arr.length-i-1]) {
temp++;
}
}
if(temp == arr.length/2) return true;
return false;
}
```
**时间复杂度:O(n) 空间复杂度:O(n)**
### 进阶:
**你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?**
**思路分析:**我们先找到链表的中间结点,然后将中间结点后面的链表进行反转,反转之后再和前半部分链表进行比较,如果相同则表示该链表属于回文链表,返回true;否则,否则返回false
### 解法二gif动画演示:
![02](../Animation/solved02.gif)
### 代码:
```java
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) return true;
ListNode p = new ListNode(-1);
ListNode low = p;
ListNode fast = p;
p.next = head;
//使用快慢指针来确定中间结点
while(fast != null && fast.next != null){
low = low.next;
fast = fast.next.next;
}
ListNode cur = low.next;
ListNode pre = null;
low.next = null;
low = p.next;
//反转后半部分链表
while(cur != null){
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
//将前半部分链表和后半部分进行比较
while(pre != null){
if(low.val != pre.val){
return false;
}
low = low.next;
pre = pre.next;
}
return true;
}
```
**时间复杂度:O(n) 空间复杂度:O(1)**
**没错,可以看到上面的代码是完全能可以通过的,虽然我们完成了题目,但是我们改变了链表的结构,也就是说它现在不是它了;出题人应该是不希望我们破坏链表的,所以在我们完成判断之后,需要将链表恢复原样,也就是将后半部分链表反转之后接到前半部分链表的末尾。**
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。 > 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
> >
> 同步个人博客:https://www.zhangxiaoshuai.fun > 同步个人博客:www.zhangxiaoshuai.fun
```txt ```txt
......
### 题目描述
两个整数之间的[汉明距离](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)
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment