Skip to content

Commit 93798da

Browse files
committed
1144
1 parent 17141fa commit 93798da

File tree

3 files changed

+222
-3
lines changed

3 files changed

+222
-3
lines changed

SUMMARY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
* [99. Recover Binary Search Tree](leetcode-99-Recover-Binary-Search-Tree.md)
103103
* [100. Same Tree](leetcode-100-Same-Tree.md)
104104
* [leetcode 100 斩!回顾](leetcode100斩回顾.md)
105-
* [101 题到 113](leetcode-101-200.md)
105+
* [101 题到 114](leetcode-101-200.md)
106106
* [101. Symmetric Tree](leetcode-101-Symmetric-Tree.md)
107107
* [102. Binary Tree Level Order Traversal](leetcode-102-Binary-Tree-Level-Order-Traversal.md)
108108
* [103. Binary Tree Zigzag Level Order Traversal](leetcode-103-Binary-Tree-Zigzag-Level-Order-Traversal.md)
@@ -115,4 +115,5 @@
115115
* [110. Balanced Binary Tree](leetcode-110-Balanced-Binary-Tree.md)
116116
* [111. Minimum Depth of Binary Tree](leetcode-111-Minimum-Depth-of-Binary-Tree.md)
117117
* [112. Path Sum](leetcode-112-Path-Sum.md)
118-
* [113. Path Sum II](leetcode-113-Path-SumII.md)
118+
* [113. Path Sum II](leetcode-113-Path-SumII.md)
119+
* [114. Flatten Binary Tree to Linked List](leetcode-114-Flatten-Binary-Tree-to-Linked-List.md)

leetcode-101-200.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@
2222

2323
<a href="leetcode-112-Path-Sum.html">112. Path Sum</a>
2424

25-
<a href="leetcode-113-Path-SumII.html">113. Path Sum II</a>
25+
<a href="leetcode-113-Path-SumII.html">113. Path Sum II</a>
26+
27+
<a href="leetcode-114-Flatten-Binary-Tree-to-Linked-List.html">114. Flatten Binary Tree to Linked List</a>
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# 题目描述(中等难度)
2+
3+
![](https://windliang.oss-cn-beijing.aliyuncs.com/114.jpg)
4+
5+
把一个二叉树展开成一个链表,展开顺序如图所示。
6+
7+
# 解法一
8+
9+
可以发现展开的顺序其实就是二叉树的先序遍历。算法和 [94 题](<https://leetcode.wang/leetCode-94-Binary-Tree-Inorder-Traversal.html#%E8%A7%A3%E6%B3%95%E4%B8%89-morris-traversal>) 中序遍历的 Morris 算法有些神似,我们需要两步完成这道题。
10+
11+
1. 将左子树插入到右子树的地方
12+
2. 将原来的右子树接到左子树的最右边节点
13+
3. 考虑新的右子树的根节点,一直重复上边的过程,直到新的右子树为 null
14+
15+
可以看图理解下这个过程。
16+
17+
```java
18+
1
19+
/ \
20+
2 5
21+
/ \ \
22+
3 4 6
23+
24+
//将 1 的左子树插入到右子树的地方
25+
1
26+
\
27+
2 5
28+
/ \ \
29+
3 4 6
30+
//将原来的右子树接到左子树的最右边节点
31+
1
32+
\
33+
2
34+
/ \
35+
3 4
36+
\
37+
5
38+
\
39+
6
40+
41+
//将 2 的左子树插入到右子树的地方
42+
1
43+
\
44+
2
45+
\
46+
3 4
47+
\
48+
5
49+
\
50+
6
51+
52+
//将原来的右子树接到左子树的最右边节点
53+
1
54+
\
55+
2
56+
\
57+
3
58+
\
59+
4
60+
\
61+
5
62+
\
63+
6
64+
65+
......
66+
```
67+
68+
代码的话也很好写,首先我们需要找出左子树最右边的节点以便把右子树接过来。
69+
70+
```java
71+
public void flatten(TreeNode root) {
72+
while (root != null) {
73+
//左子树为 null,直接考虑下一个节点
74+
if (root.left == null) {
75+
root = root.right;
76+
} else {
77+
// 找左子树最右边的节点
78+
TreeNode pre = root.left;
79+
while (pre.right != null) {
80+
pre = pre.right;
81+
}
82+
//将原来的右子树接到左子树的最右边节点
83+
pre.right = root.right;
84+
// 将左子树插入到右子树的地方
85+
root.right = root.left;
86+
root.left = null;
87+
// 考虑下一个节点
88+
root = root.right;
89+
}
90+
}
91+
}
92+
```
93+
94+
# 解法二
95+
96+
题目中,要求说是`in-place`,之前一直以为这个意思就是要求空间复杂度是`O(1)`。偶然看见评论区 [StefanPochmann](https://leetcode.com/stefanpochmann) 大神的解释。
97+
98+
![](https://windliang.oss-cn-beijing.aliyuncs.com/114_2.jpg)
99+
100+
也就是说`in-place` 的意思可能更多说的是直接在原来的节点上改变指向,空间复杂度并没有要求。所以这道题也可以用递归解一下,参考 [这里](<https://leetcode.com/problems/flatten-binary-tree-to-linked-list/discuss/36977/My-short-post-order-traversal-Java-solution-for-share>)
101+
102+
```java
103+
1
104+
/ \
105+
2 5
106+
/ \ \
107+
3 4 6
108+
```
109+
110+
利用递归的话,可能比解法一难理解一些。
111+
112+
题目其实就是将二叉树通过右指针,组成一个链表。
113+
114+
```java
115+
1 -> 2 -> 3 -> 4 -> 5 -> 6
116+
```
117+
118+
我们知道题目给定的遍历顺序其实就是先序遍历的顺序,所以我们能不能利用先序遍历的代码,每遍历一个节点,就将上一个节点的右指针更新为当前节点。
119+
120+
先序遍历的顺序是`1 2 3 4 5 6`
121+
122+
遍历到`2`,把`1`的右指针指向`2``1 -> 2 3 4 5 6`
123+
124+
遍历到`3`,把`2`的右指针指向`3``1 -> 2 -> 3 4 5 6`
125+
126+
... ...
127+
128+
一直进行下去似乎就解决了这个问题。但现实是残酷的,原因就是我们把`1`的右指针指向`2`,那么`1`的原本的右孩子就丢失了,也就是`5` 就找不到了。
129+
130+
解决方法的话,我们可以逆过来进行。
131+
132+
我们依次遍历`6 5 4 3 2 1`,然后每遍历一个节点就将当前节点的右指针更新为上一个节点。
133+
134+
遍历到`5`,把`5`的右指针指向`6``6 <- 5 4 3 2 1`
135+
136+
遍历到`4`,把`4`的右指针指向`5``6 <- 5 <- 4 3 2 1`
137+
138+
... ...
139+
140+
```java
141+
1
142+
/ \
143+
2 5
144+
/ \ \
145+
3 4 6
146+
```
147+
148+
这样就不会有丢失孩子的问题了,因为更新当前的右指针的时候,当前节点的右孩子已经访问过了。
149+
150+
`6 5 4 3 2 1`的遍历顺序其实变形的后序遍历,遍历顺序是右子树->左子树->根节点。
151+
152+
先回想一下后序遍历的代码
153+
154+
```java
155+
public void PrintBinaryTreeBacRecur(TreeNode<T> root){
156+
if (root == null)
157+
return;
158+
159+
PrintBinaryTreeBacRecur(root.right);
160+
PrintBinaryTreeBacRecur(root.left);
161+
System.out.print(root.data);
162+
163+
}
164+
```
165+
166+
这里的话,我们不再是打印根节点,而是利用一个全局变量`pre`,更新当前根节点的右指针为`pre`,左指针为`null`
167+
168+
```java
169+
private TreeNode pre = null;
170+
171+
public void flatten(TreeNode root) {
172+
if (root == null)
173+
return;
174+
flatten(root.right);
175+
flatten(root.left);
176+
root.right = pre;
177+
root.left = null;
178+
pre = root;
179+
}
180+
```
181+
182+
相应的左孩子也要置为`null`,同样的也不用担心左孩子丢失,因为是后序遍历,左孩子已经遍历过了。和 [112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 一样,都巧妙的利用了后序遍历。
183+
184+
既然后序遍历这么有用,利用 [112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 介绍的后序遍历的迭代方法,把这道题也改一下吧。
185+
186+
```java
187+
public void flatten(TreeNode root) {
188+
Stack<TreeNode> toVisit = new Stack<>();
189+
TreeNode cur = root;
190+
TreeNode pre = null;
191+
192+
while (cur != null || !toVisit.isEmpty()) {
193+
while (cur != null) {
194+
toVisit.push(cur); // 添加根节点
195+
cur = cur.right; // 递归添加右节点
196+
}
197+
cur = toVisit.peek(); // 已经访问到最右的节点了
198+
// 在不存在左节点或者右节点已经访问过的情况下,访问根节点
199+
if (cur.left == null || cur.left == pre) {
200+
toVisit.pop();
201+
/**************修改的地方***************/
202+
cur.right = pre;
203+
cur.left = null;
204+
/*************************************/
205+
pre = cur;
206+
cur = null;
207+
} else {
208+
cur = cur.left; // 左节点还没有访问过就先访问左节点
209+
}
210+
}
211+
}
212+
```
213+
214+
#
215+
216+
其实两种解法就是从两种方向解决问题,解法一自顶向下,解法二自底向上。以前觉得后序遍历比较麻烦,没想到竟然连续遇到了后序遍历的应用。

0 commit comments

Comments
 (0)