LeetCode#199 二叉樹的右視圖 Java

@author: sdubrz
@date: 2020.04.22
題號: 199
題目難度: 中等
考察內容: 樹
原題鏈接 https://leetcode-cn.com/problems/binary-tree-right-side-view/
題目及官方解法的著作權歸領釦網絡所有,商業轉載請聯繫官方授權,非商業轉載請註明出處。
解題代碼轉載請聯繫 lwyz521604#163.com

給定一棵二叉樹,想象自己站在它的右側,按照從頂部到底部的順序,返回從右側所能看到的節點值。

示例:

輸入: [1,2,3,null,5,null,4]
輸出: [1, 3, 4]
解釋:

   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---

通過次數28,313 提交次數44,128

遞歸解法

首先,拿到這道題的初步感覺是,必須要遍歷所有的節點,因而時間複雜度不太可能低於 O(n)O(n)

這道題可以用遞歸來做。以節點A爲根節點的子樹的右視圖可以根據它的左子樹的右視圖和右子樹的右視圖來得到。如果右子樹的右視圖鏈表長度不小於左子樹的右視圖鏈表,那麼整棵樹的鏈表直接加上右子樹的鏈表就可以了。否則,需要加上左子樹鏈表中後面的幾個。

下面是用 Java 實現的代碼

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
 import java.util.*;
class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        if(root==null)
            return list;
        
        list.add(root.val);
        List<Integer> list1 = rightSideView(root.right);
        List<Integer> list2 = rightSideView(root.left);

        list.addAll(list1);

        if(list1.size()<list2.size()){
            for(int i=list1.size(); i<list2.size(); i++){
                list.add(list2.get(i));
            }
        }

        return list;
    }
}

下面是在 LeetCode 系統中提交的結果

執行結果: 通過 顯示詳情
執行用時 : 1 ms, 在所有 Java 提交中擊敗了 97.39% 的用戶
內存消耗 : 38.7 MB, 在所有 Java 提交中擊敗了 5.00% 的用戶

官方解法1:深度優先搜索

官方給出題解中也說明了由於樹的形狀無法提前知曉,不可能設計出由於 O(n)O(n) 的算法這一點。官方分別用 DFS 和 BFS 給出瞭解法。下面是官方給出的解法。

思路

我們對樹進行深度優先搜索,在搜索過程中,我們總是先訪問右子樹。那麼對於每一層來說,我們在這層見到的第一個結點一定是最右邊的結點。

算法

這樣一來,我們可以存儲在每個深度訪問的第一個結點,一旦我們知道了樹的層數,就可以得到最終的結果數組。

DFS
上圖表示了問題的一個實例。紅色結點自上而下組成答案,邊緣以訪問順序標號。

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Map<Integer, Integer> rightmostValueAtDepth = new HashMap<Integer, Integer>();
        int max_depth = -1;

        Stack<TreeNode> nodeStack = new Stack<TreeNode>();
        Stack<Integer> depthStack = new Stack<Integer>();
        nodeStack.push(root);
        depthStack.push(0);

        while (!nodeStack.isEmpty()) {
            TreeNode node = nodeStack.pop();
            int depth = depthStack.pop();

            if (node != null) {
            	// 維護二叉樹的最大深度
                max_depth = Math.max(max_depth, depth);

                // 如果不存在對應深度的節點我們才插入
                if (!rightmostValueAtDepth.containsKey(depth)) {
                    rightmostValueAtDepth.put(depth, node.val);
                }

                nodeStack.push(node.left);
                nodeStack.push(node.right);
                depthStack.push(depth+1);
                depthStack.push(depth+1);
            }
        }

        List<Integer> rightView = new ArrayList<Integer>();
        for (int depth = 0; depth <= max_depth; depth++) {
            rightView.add(rightmostValueAtDepth.get(depth));
        }

        return rightView;
    }
}

複雜度分析

  • 時間複雜度 : O(n)O(n)。深度優先搜索最多訪問每個結點一次,因此是線性複雜度。

  • 空間複雜度 : O(n)O(n)。最壞情況下,棧內會包含接近樹高度的結點數量,佔用 O(n)O(n) 的空間。

官方解法2:廣度優先搜索

思路

我們可以對二叉樹進行層次遍歷,那麼對於每層來說,最右邊的結點一定是最後被遍歷到的。二叉樹的層次遍歷可以用廣度優先搜索實現。

算法

執行廣度優先搜索,左結點排在右結點之前,這樣,我們對每一層都從左到右訪問。因此,只保留每個深度最後訪問的結點,我們就可以在遍歷完整棵樹後得到每個深度最右的結點。除了將棧改成隊列,並去除了rightmost_value_at_depth之前的檢查外,算法沒有別的改動。

BFS

上圖表示了同一個示例,紅色結點自上而下組成答案,邊緣以訪問順序標號。

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Map<Integer, Integer> rightmostValueAtDepth = new HashMap<Integer, Integer>();
        int max_depth = -1;

        Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();
        Queue<Integer> depthQueue = new LinkedList<Integer>();
        nodeQueue.add(root);
        depthQueue.add(0);

        while (!nodeQueue.isEmpty()) {
            TreeNode node = nodeQueue.remove();
            int depth = depthQueue.remove();

            if (node != null) {
            	// 維護二叉樹的最大深度
                max_depth = Math.max(max_depth, depth);

                // 由於每一層最後一個訪問到的節點纔是我們要的答案,因此不斷更新對應深度的信息即可
                rightmostValueAtDepth.put(depth, node.val);

                nodeQueue.add(node.left);
                nodeQueue.add(node.right);
                depthQueue.add(depth+1);
                depthQueue.add(depth+1);
            }
        }

        List<Integer> rightView = new ArrayList<Integer>();
        for (int depth = 0; depth <= max_depth; depth++) {
            rightView.add(rightmostValueAtDepth.get(depth));
        }

        return rightView;
    }
}

複雜度分析

時間複雜度 : O(n)O(n)。 每個節點最多進隊列一次,出隊列一次,因此廣度優先搜索的複雜度爲線性。

空間複雜度 : O(n)O(n)。每個節點最多進隊列一次,所以隊列長度最大不不超過 nn,所以這裏的空間代價爲 O(n)O(n)

本文是在 LeetCode 刷題過程中記錄的筆記,如有不足之處歡迎通過留言或QQ賜教。

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