演算法 最小高度樹

Novice-XiaoSong發表於2020-11-25

leetcode 310 題 [medium]
中:最小高度樹
英: Minimum Height Trees
在這裡插入圖片描述在這裡插入圖片描述

題解

核心:拓撲排序法
時間複雜度:O(n*minH)
空間複雜度:O(n)

/**
 * @param {number} n
 * @param {number[][]} edges
 * @return {number[]}
 */
var findMinHeightTrees = function(n, edges) {
    /**
     * 拓撲排序法
     * 步驟:每次刪除所有當前入度為1的結點,直到最後剩下的結點數 <= 2
     * 原理:
     * 1. 刪除入度為1的結點:相當於剪去所有當前的葉節點,最小高度樹的層數必然(僅)下降1層
     * (1) 所以最小高度樹的高度等於(刪除次數 + 剩下結點數)(假設只有一個結點時高度為1)
     * 2. 最後結點 <= 2
     * (1) 最後剩下1個結點:那麼該結點為唯一解
     * (2) 最後剩下2個結點:以這兩個結點為根的樹都是最小高度樹
     * (3) 最後剩下3個結點:那麼必然有兩個入度為1的結點和一個入度大於1的結點,可再次通過刪除所有當前入度為1的結點進而使得樹的高度(僅)下降1層
     * (4) 最後剩下結點>3,與(3)同理,可以通過剪枝使樹高度下降
     * 
     * 時間複雜度:O(n*minH):O(n)(構建入度陣列(鄰接列表))+ ≈O(n)*O(minH)(迴圈刪除入度為1的結點) + O(n)(計算結果) => O(n*minH)
     * 空間複雜度:O(n):O(2*(n-1))(鄰接列表大小,其實就等於edges中的結點個數) => O(n)
     */
    // 初始化入度陣列(其實就是鄰接列表)
    let inDegreeArr = Array.from({length: n}, ()=>[]);
    for(let i=0, len=edges.length; i<len; i++){
        let item = edges[i];
        inDegreeArr[item[0]].push(item[1]);
        inDegreeArr[item[1]].push(item[0]);
    }

    // 迴圈刪除入度為1的結點
    let restNodesCount = n;
    while(restNodesCount > 2){
        let needToDelete = [];
        for(let i=0; i<n; i++){
            if(inDegreeArr[i] && inDegreeArr[i].length === 1){
                needToDelete.push(i);
            }
        }
        // 一次性刪除
        for(let i=0, len=needToDelete.length; i<len; i++){
            let node = needToDelete[i];
            let connected = inDegreeArr[node][0];  // 連線的結點
            inDegreeArr[node] = null;              // 標識為已刪除
            inDegreeArr[connected].splice(inDegreeArr[connected].indexOf(node), 1);
            restNodesCount--;
        }
    }
    // 結果
    let result = [];
    for(let i=0; i<n; i++){
        inDegreeArr[i] && result.push(i);
    }
    return result;
};

相關文章