題目描述(中等難度)
給定一個二叉樹,然後每個節點有一個 next
指針,將它指向它右邊的節點。和 116 題 基本一樣,區別在於之前是滿二叉樹。
解法一 BFS
直接把 116 題 題的代碼複製過來就好,一句也不用改。
利用一個棧將下一層的節點保存。通過pre
指針把棧裏的元素一個一個接起來。
public Node connect(Node root) {
if (root == null) {
return root;
}
Queue<Node> queue = new LinkedList<Node>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
Node pre = null;
for (int i = 0; i < size; i++) {
Node cur = queue.poll();
if (i > 0) {
pre.next = cur;
}
pre = cur;
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
}
return root;
}
解法二
當然題目要求了空間複雜度,可以先到 116 題 看一下思路,這裏在上邊的基礎上改一下。
我們用第二種簡潔的代碼,相對會好改一些。
Node connect(Node root) {
if (root == null)
return root;
Node pre = root;
Node cur = null;
while (pre.left != null) {
cur = pre;
while (cur != null) {
cur.left.next = cur.right;
if (cur.next != null) {
cur.right.next = cur.next.left;
}
cur = cur.next;
}
pre = pre.left;
}
return root;
}
需要解決的問題還是挺多的。
cur.left.next = cur.right;
cur.right.next = cur.next.left;
之前的關鍵代碼就是上邊兩句,但是在這道題中我們無法保證cur.left
或者 cur.right
或者 cur.next.left
或者cur.next.right
是否爲null
。所以我們需要用一個while
循環來保證當前節點至少有一個孩子。
while (cur.left == null && cur.right == null) {
cur = cur.next;
}
這樣的話保證了當前節點至少有一個孩子,然後如果一個孩子爲 null
,那麼就可以保證另一個一定不爲 null
了。
整體的話,就用了上邊介紹的技巧,代碼比較長,可以結合的看一下。
Node connect(Node root) {
if (root == null)
return root;
Node pre = root;
Node cur = null;
while (true) {
cur = pre;
while (cur != null) {
//找到至少有一個孩子的節點
if (cur.left == null && cur.right == null) {
cur = cur.next;
continue;
}
//找到當前節點的下一個至少有一個孩子的節點
Node next = cur.next;
while (next != null && next.left == null && next.right == null) {
next = next.next;
if (next == null) {
break;
}
}
//當前節點的左右孩子都不爲空,就將 left.next 指向 right
if (cur.left != null && cur.right != null) {
cur.left.next = cur.right;
}
//要接上 next 的節點的孩子,所以用 temp 處理當前節點 right 爲 null 的情況
Node temp = cur.right == null ? cur.left : cur.right;
if (next != null) {
//next 左孩子不爲 null,就接上左孩子。
if (next.left != null) {
temp.next = next.left;
//next 左孩子爲 null,就接上右孩子。
} else {
temp.next = next.right;
}
}
cur = cur.next;
}
//找到擁有孩子的節點
while (pre.left == null && pre.right == null) {
pre = pre.next;
//都沒有孩子說明已經是最後一層了
if (pre == null) {
return root;
}
}
//進入下一層
pre = pre.left != null ? pre.left : pre.right;
}
}
解法三
參考 這裏。
利用解法一的思想,我們利用 pre
指針,然後一個一個取節點,把它連起來。解法一爲什麼沒有像解法二那樣考慮當前節點爲 null
呢?因爲我們沒有添加爲 null
的節點,就是下邊的代碼的作用。
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
所以這裏是一樣的,如果當前節點爲null
不處理就可以了。
第二個問題,怎麼得到每次的開頭的節點呢?我們用一個dummy
指針,當連接第一個節點的時候,就將dummy
指針指向他。此外,之前用的pre
指針,把它當成tail
指針可能會更好理解。如下圖所示:
cur
指針利用 next
不停的遍歷當前層。
如果 cur
的孩子不爲 null
就將它接到 tail
後邊,然後更新tail
。
當 cur
爲 null
的時候,再利用 dummy
指針得到新的一層的開始節點。
dummy
指針在鏈表中經常用到,他只是爲了處理頭結點的情況,它並不屬於當前鏈表。
代碼就異常的簡單了。
Node connect(Node root) {
Node cur = root;
while (cur != null) {
Node dummy = new Node();
Node tail = dummy;
//遍歷 cur 的當前層
while (cur != null) {
if (cur.left != null) {
tail.next = cur.left;
tail = tail.next;
}
if (cur.right != null) {
tail.next = cur.right;
tail = tail.next;
}
cur = cur.next;
}
//更新 cur 到下一層
cur = dummy.next;
}
return root;
}
總
本來爲了圖方便,在 116 題 的基礎上把解法二改了出來,還搞了蠻久,因爲爲 null
的情況太多了,不停的報空指針異常,最後終於理清了思路。但和解法三比起來實在是相形見絀了,解法三太優雅了,但其實這纔是正常的思路,從解法一的做法產生靈感,利用 tail
指針將它們連起來。
更多詳細通俗題解詳見 leetcode.wang 。