【連結串列問題】打卡10:將搜尋二叉樹轉換成雙向連結串列

帥地發表於2019-02-24

前言

以專題的形式更新刷題貼,歡迎跟我一起學習刷題,相信我,你的堅持,絕對會有意想不到的收穫。每道題會提供簡單的解答,如果你有更優雅的做法,歡迎提供指點,謝謝。

注:如果程式碼排版出現了問題麻煩通知我下,謝謝。

【題目描述】

對於二叉樹的節點來說,有本身的值域,有指向左孩子和右孩子的兩個指標;對雙向連結串列的節點來說,有本身的值域,有指向上一個節點和下一個節點的指標。在結構上,兩種結構有相似性,現有一棵搜尋二叉樹,請將其轉為成一個有序的雙向連結串列。
 
節點定義:

class Node2{
    public int value;
    public Node2 left;
    public Node2 right;

    public Node2(int value) {
        this.value = value;
    }
}

例如:

【連結串列問題】打卡10:將搜尋二叉樹轉換成雙向連結串列

這棵二查搜尋樹轉換後的雙向連結串列從頭到尾依次是 1~9。對於每一個節點來說,原來的 right 指標等價於轉換後的 next 指標,原來的 left 指標等價於轉換後的 last 指標,最後返回轉換後的雙向連結串列的頭節點。

【要求】

如果連結串列的長度為 N, 時間複雜度達到 O(N)。

【難度】

尉:★★☆☆

【解答】

方法一:採用佇列輔助

如果用一個佇列來輔助的話,還是挺容易。採用中序遍歷的方法,把二叉樹的節點全部放進佇列,之後在逐一彈出來連線成雙向連結串列。

程式碼如下

    public static Node2 convert1(Node2 head) {
        Queue<Node2> queue = new LinkedList<>();
        //將節點按中序遍歷放進佇列裡
        inOrderToQueue(head, queue);
        head = queue.poll();
        Node2 pre = head;
        pre.left = null;
        Node2 cur = null;
        while (!queue.isEmpty()) {
            cur = queue.poll();
            pre.right = cur;
            cur.left = pre;
            pre = cur;
        }
        pre.right = null;
        return head;
    }

    private static void inOrderToQueue(Node2 head, Queue<Node2> queue) {
        if (head == null) {
            return;
        }
        inOrderToQueue(head.left, queue);
        queue.offer(head);
        inOrderToQueue(head.right, queue);
    }

這種方法的時間複雜度為 O(n), 空間複雜度也為 O(n)。

方法2:通過遞迴的方式

在之前打卡的9道題中,幾乎超過一般都用到了遞迴,如果這些題目使用的遞迴大家都理解了,並且能夠自己獨立寫出程式碼了,那麼我相信大家對遞迴的思想、使用已經有一定的熟練性。

我們假設函式conver的功能就是把二叉樹變成雙向連結串列,例如對於這種一棵二叉樹:

【連結串列問題】打卡10:將搜尋二叉樹轉換成雙向連結串列

經過conver轉換後變成這樣:

【連結串列問題】打卡10:將搜尋二叉樹轉換成雙向連結串列

注意,轉換之後,把最右邊節點的right指標指向了最左邊的節點的。

對於下面這樣一顆二叉樹:

【連結串列問題】打卡10:將搜尋二叉樹轉換成雙向連結串列

採用conver函式分別對左右子樹做處理,結果如下:

【連結串列問題】打卡10:將搜尋二叉樹轉換成雙向連結串列

之後,再把他們連線起來

【連結串列問題】打卡10:將搜尋二叉樹轉換成雙向連結串列

瞭解了基本原理之後,直接看程式碼吧。

    public static Node2 conver(Node2 head) {
        if (head == null) {
            return head;
        }
        Node2 leftE = conver(head.left);
        Node2 rightE = conver(head.right);
        Node2 leftB = leftE != null ? leftE.right : null;
        Node2 rightB = rightE != null ? rightE.right : null;
        if (leftE != null && rightE != null) {
            leftE.right = head;
            head.left = leftE;
            head.right = rightB;
            rightB.left = head;
            rightE.right = leftB;
            return rightE;
        } else if (leftE != null) {
            leftE.right = head;
            head.left = leftE;
            head.right = leftB;
            return head;
        } else if (rightE != null) {
            head.right = rightB;
            rightB.left = head;
            rightE.right = head;
            return rightE;
        } else {
            head.right = head;
            return head;
        }
    }
    

時間複雜度為O(n),空間複雜度為O(h),其中h是二叉樹的高度。

原理雖然不難,但寫起程式碼,還是有挺多細節需要注意的,所以一直強調,有時間的話,一定要自己手打一遍程式碼,有時你以為自己懂了,可能在寫程式碼的時候,發現自己並沒有懂,一寫就出現很多bug。

【連結串列問題】打卡9:將單連結串列的每K個節點之間逆序

【連結串列問題】打卡8:複製含有隨機指標節點的連結串列

【連結串列問題】打卡7:將單向連結串列按某值劃分成左邊小,中間相等,右邊大的形式

最後推廣下我的公眾號:苦逼的碼農,文章都會首發於我的公眾號,期待各路英雄的關注交流。

相關文章