LeetCode 力扣 117. 填充每個節點的下一個右側節點指針 II

題目描述(中等難度)

給定一個二叉樹,然後每個節點有一個 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

curnull 的時候,再利用 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章