Skip to content

Commit 80480ba

Browse files
committed
138
1 parent c6d3330 commit 80480ba

File tree

4 files changed

+236
-4
lines changed

4 files changed

+236
-4
lines changed

SUMMARY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
* [98. Validate Binary Search Tree](leetCode-98-Validate-Binary-Search-Tree.md)
103103
* [99. Recover Binary Search Tree](leetcode-99-Recover-Binary-Search-Tree.md)
104104
* [100. Same Tree](leetcode-100-Same-Tree.md)
105-
* [101 题到 137 题](leetcode-101-200.md)
105+
* [101 题到 138题](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)
@@ -139,4 +139,5 @@
139139
* [134. Gas Station](leetcode-134-Gas-Station.md)
140140
* [135. Candy](leetcode-135-Candy.md)
141141
* [136. Single Number](leetcode-136-Single-Number.md)
142-
* [137*. Single Number II](leetcode-137-Single-NumberII.md)
142+
* [137*. Single Number II](leetcode-137-Single-NumberII.md)
143+
* [138. Copy List with Random Pointer](leetcode-138-Copy-List-with-Random-Pointer.md)

leetCode-22-Generate-Parentheses.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![](https://windliang.oss-cn-beijing.aliyuncs.com/22.jpg)
44

5-
给一个数字 n ,返回所有合法的括号匹配,刚好和[20题](https://leetcode.windliang.cc/leetCode-21-Merge-Two-Sorted-Lists.html)相反。
5+
给一个数字 n ,返回所有合法的括号匹配,刚好和[20题](https://leetcode.wang/leetCode-20-Valid%20Parentheses.html)相反。
66

77
自己没想出来,全部参考 LeetCode 给出的 [Solution](https://leetcode.com/problems/generate-parentheses/solution/)
88

leetcode-101-200.md

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

7171
<a href="leetcode-136-Single-Number.html">136. Single Number</a>
7272

73-
<a href="leetcode-137-Single-NumberII.html">137. Single Number II</a>
73+
<a href="leetcode-137-Single-NumberII.html">137. Single Number II</a>
74+
75+
<a href="leetcode-138-Copy-List-with-Random-Pointer.html">138. Copy List with Random Pointer</a>
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
# 题目描述(中等难度)
2+
3+
![](https://windliang.oss-cn-beijing.aliyuncs.com/138.jpg)
4+
5+
给一个链表,返回复制后的链表。链表节点相对于普通的多了一个 `random` 指针,会随机指向链表内的任意节点或者指向 `null`
6+
7+
# 思路分析
8+
9+
这道题其实和 [133 题](https://leetcode.wang/leetcode-133-Clone-Graph.html) 复制一个图很类似,这里的话就是要解决的问题就是,当更新当前节点的 `random` 指针的时候,如果 `random` 指向的是很后边的节点,但此时后边的节点还没有生成,那么我们该如何处理。
10+
11+
[133 题](https://leetcode.wang/leetcode-133-Clone-Graph.html) 一样,我们可以利用 `HashMap` 将节点提前生成并且保存起来,第二次遍历到他的时候直接从 `HashMap` 里边拿即可。
12+
13+
这里的话就有两种思路,一种需要遍历两边链表,一种只需要遍历一遍。
14+
15+
# 解法一
16+
17+
首先利用 `HashMap` 来一个不用思考的代码。
18+
19+
遍历第一遍链表,我们不考虑链表之间的相互关系,仅仅生成所有节点,然后把它存到 `HashMap` 中,`val` 作为 `key``Node` 作为 `value`
20+
21+
遍历第二遍链表,将之前生成的节点取出来,更新它们的 `next``random` 指针。
22+
23+
```java
24+
public Node copyRandomList(Node head) {
25+
if (head == null) {
26+
return null;
27+
}
28+
HashMap<Integer, Node> map = new HashMap<>();
29+
Node h = head;
30+
//生成所有节点
31+
while (h != null) {
32+
Node t = new Node();
33+
t.val = h.val;
34+
map.put(t.val, t);
35+
h = h.next;
36+
}
37+
h = head;
38+
//更新 next 和 random
39+
while (h != null) {
40+
if (h.next != null) {
41+
map.get(h.val).next = map.get(h.next.val);
42+
}
43+
if (h.random != null) {
44+
map.get(h.val).random = map.get(h.random.val);
45+
}
46+
h = h.next;
47+
}
48+
return map.get(head.val);
49+
}
50+
```
51+
52+
# 解法二
53+
54+
解法一虽然简单易懂,但还是有可以优化的地方的。我们可以只遍历一次链表。
55+
56+
核心思想就是延迟更新它的 `next`
57+
58+
```java
59+
1 -> 2 -> 3
60+
61+
用 cur 指向已经生成的节点的末尾
62+
1 -> 2
63+
^
64+
c
65+
66+
然后将 3 构造完成
67+
68+
最后将 2 的 next 指向 3
69+
1 -> 2 -> 3
70+
^
71+
c
72+
73+
期间已经生成的节点存到 HashMap 中,第二次遇到的时候直接从 HashMap 中拿
74+
```
75+
76+
看下代码理解一下含义吧
77+
78+
```java
79+
public Node copyRandomList(Node head) {
80+
if (head == null) {
81+
return null;
82+
}
83+
HashMap<Integer, Node> map = new HashMap<>();
84+
Node h = head;
85+
Node cur = new Node(); //空结点,dummy 节点,为了方便头结点计算
86+
while (h != null) {
87+
//判断当前节点是否已经产生过
88+
if (!map.containsKey(h.val)) {
89+
Node t = new Node();
90+
t.val = h.val;
91+
map.put(t.val, t);
92+
}
93+
//得到当前节点去更新它的 random 指针
94+
Node next = map.get(h.val);
95+
if (h.random != null) {
96+
//判断当前节点是否已经产生过
97+
if (!map.containsKey(h.random.val)) {
98+
next.random = new Node();
99+
next.random.val = h.random.val;
100+
map.put(next.random.val, next.random);
101+
} else {
102+
next.random = map.get(h.random.val);
103+
}
104+
105+
}
106+
//将当前生成的节点接到 cur 的后边
107+
cur.next = next;
108+
cur = cur.next;
109+
h = h.next;
110+
}
111+
return map.get(head.val);
112+
}
113+
```
114+
115+
# 解法三
116+
117+
上边的两种解法都用到了 `HashMap` ,所以额外需要 `O(n)` 的空间复杂度。现在考虑不需要额外空间的方法。
118+
119+
主要参考了[这里](https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N))。主要解决的问题就是我们生成节点以后,当更新它的 `random` 的时候,怎么找到之前生成的节点,前两种解法用了 `HashMap` 全部存起来,这里的话可以利用原来的链表的指针域。
120+
121+
主要需要三步。
122+
123+
1. 生成所有的节点,并且分别插入到原有节点的后边
124+
2. 更新插入节点的 `random`
125+
3. 将新旧节点分离开来
126+
127+
一图胜千言,大家看一下下边的图吧。
128+
129+
![](https://windliang.oss-cn-beijing.aliyuncs.com/138_2.jpg)
130+
131+
代码对应如下。
132+
133+
```java
134+
public Node copyRandomList(Node head) {
135+
if (head == null) {
136+
return null;
137+
}
138+
Node l1 = head;
139+
Node l2 = null;
140+
//生成所有的节点,并且分别插入到原有节点的后边
141+
while (l1 != null) {
142+
l2 = new Node();
143+
l2.val = l1.val;
144+
l2.next = l1.next;
145+
l1.next = l2;
146+
l1 = l1.next.next;
147+
}
148+
//更新插入节点的 random
149+
l1 = head;
150+
while (l1 != null) {
151+
if (l1.random != null) {
152+
l1.next.random = l1.random.next;
153+
}
154+
l1 = l1.next.next;
155+
}
156+
157+
l1 = head;
158+
Node l2_head = l1.next;
159+
//将新旧节点分离开来
160+
while (l1 != null) {
161+
l2 = l1.next;
162+
l1.next = l2.next;
163+
if (l2.next != null) {
164+
l2.next = l2.next.next;
165+
}
166+
l1 = l1.next;
167+
}
168+
return l2_head;
169+
}
170+
```
171+
172+
# 解法四
173+
174+
不利用额外的空间复杂度还有一种思路,参考 [这里](https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43497/2-clean-C%2B%2B-algorithms-without-using-extra-arrayhash-table.-Algorithms-are-explained-step-by-step.)
175+
176+
解法三利用原链表的 `next` 域把新生成的节点保存了起来。类似的,我们还可以利用原链表的 `random` 域把新生成的节点保存起来。
177+
178+
主要还是三个步骤。
179+
180+
1. 生成所有的节点,将它们保存到原链表的 `random` 域,同时利用新生成的节点的 `next` 域保存原链表的 `random`
181+
2. 更新新生成节点的 `random` 指针。
182+
3. 恢复原链表的 `random` 指针,同时更新新生成节点的 `next` 指针。
183+
184+
一图胜千言。
185+
186+
![](https://windliang.oss-cn-beijing.aliyuncs.com/138_3.jpg)
187+
188+
相应的代码如下。
189+
190+
```java
191+
public Node copyRandomList(Node head) {
192+
if (head == null) {
193+
return null;
194+
}
195+
Node l1 = head;
196+
Node l2 = null;
197+
//生成所有的节点,讲它们保存到原链表的 random 域,
198+
//同时利用新生成的节点的 next 域保存原链表的 random。
199+
while (l1 != null) {
200+
l2 = new Node();
201+
l2.val = l1.val;
202+
l2.next = l1.random;
203+
l1.random = l2;
204+
l1 = l1.next;
205+
}
206+
l1 = head;
207+
//更新新生成节点的 random 指针。
208+
while (l1 != null) {
209+
l2 = l1.random;
210+
l2.random = l2.next != null ? l2.next.random : null;
211+
l1 = l1.next;
212+
}
213+
214+
l1 = head;
215+
Node l2_head = l1.random;
216+
//恢复原链表的 random 指针,同时更新新生成节点的 next 指针。
217+
while (l1 != null) {
218+
l2 = l1.random;
219+
l1.random = l2.next;
220+
l2.next = l1.next != null ? l1.next.random : null;
221+
l1 = l1.next;
222+
}
223+
return l2_head;
224+
}
225+
```
226+
227+
#
228+
229+
解法一、解法二是比较直接的想法,直接利用 `HashMap` 存储之前的节点。解法三、解法四利用原有链表的指针,通过指来指去完成了赋值。链表操作的核心思想就是,在改变某一个节点的指针域的时候,一定要把该节点的指针指向的节点用另一个指针保存起来,以免造成丢失。

0 commit comments

Comments
 (0)