310. Minimum Height Trees

For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels.

Format
The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels).
You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Example 1:
Given n = 4, edges = [[1, 0], [1, 2], [1, 3]]

        0
        |
        1
       / \
      2   3
return [1]
Example 2:
Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]

     0  1  2
      \ | /
        3
        |
        4
        |
        5
return [3, 4]

Note:
(1) According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactlyone path. In other words, any connected graph without simple cycles is a tree.”
(2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.

Solution1:Literally find Longest Path by BFS

思路: 隨便選一點a,從a bfs先找出距a的最遠的u,再從u點bfs找出最遠點v,u到v的path的一半處就是result.
Time Complexity: O(N) Space Complexity: O(N)
Note: DFS 1.b 這這里其實也可以

Solution2:BFS topological sort

思路: 類似Undirected拓撲排序對策BFS方法,從pointers所有leaves向內,當兩個pointers met,only keep one of them, until the last two pointers meet or one step away we then find the roots.

(1) 在做UnDirected 拓撲排序時,要避免訪問前驅:
實現1:采用在adj_list.get(cur).remove(prev)的方式,因為(1),所以remove是O(1), 且這樣就不需要使用indegree數組,adj_list.get(cur).size()就可以作為indegree作用。
(實現2: 不remove,通過visited數組,但額外需要indegree數組,因為adj_list.get(cur).size()不能再作為indegree作用??梢詉ndegree數組/visited數組通過共同一個數組表示。)因為下述(2),肯定是訪問到的是 prev->cur->next形式,所以remove是O(1),實現1比較好

(2) 在做UnDirected 拓撲排序時,訪問到的indegree = 1的node,最多肯定只有一個前驅和一個后繼,所以不需要遍歷list來拿到nexts,只有一個next,直接可以通過adj.get(u).iterator().next();

We start from every end, by end we mean vertex of degree 1 (aka leaves). We let the pointers move the same speed. When two pointers meet, we keep only one of them, until the last two pointers meet or one step away we then find the roots.
It is easy to see that the last two pointers are from the two ends of the longest path in the graph.
The actual implementation is similar to the BFS topological sort. Remove the leaves, update the degrees of inner vertexes. Then remove the new leaves. Doing so level by level until there are 2 or 1 nodes left. What's left is our answer!

Time Complexity: O(N) Space Complexity: O(N)

Solution3:Tree DP??

思路: (temporarily skip)
reference: https://leetcode.com/problems/minimum-height-trees/discuss/
Time Complexity: O(N) Space Complexity: O(N)

Solution1 Code:

public class Solution {
    int n;
    List<List<Integer>> graph;

    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        if (n <= 0) return new ArrayList<>();
        this.n = n;
        
        // build graph
        graph = new ArrayList<List<Integer>>();
        for(int i = 0; i < n; i++) {
            graph.add(new ArrayList<Integer>());
        }
        for (int[] pair : edges) {
            int u = pair[0];
            int v = pair[1];
            graph.get(u).add(v);
            graph.get(v).add(u);
        }
        
        int[] dist1 = new int[n]; // dist for all node from u
        int[] dist2 = new int[n]; // dist for all node from v
        int[] pre = new int[n]; // precursor array: v: u
        
        // get far end1 = u
        bfs(0, dist1, pre);
        int u = 0;
        for (int i = 0; i < n; i++)
            if (dist1[i] > dist1[u]) u = i;

        // get far end2 = v
        bfs(u, dist2, pre);
        int v = 0;
        for (int i = 0; i < n; i++)
            if (dist2[i] > dist2[v]) v = i;

        // prepare the result: add path from u to v to list
        List<Integer> list = new ArrayList<>();
        while (v != -1) {
            list.add(v);
            v = pre[v];
        }

        // return the result
        if (list.size() % 2 == 1) return Arrays.asList(list.get(list.size() / 2));
        else return Arrays.asList(list.get(list.size() / 2 - 1), list.get(list.size() / 2));
    }
    
    
    private void bfs(int start, int[] dist, int[] pre) {
        boolean[] visited = new boolean[n];
        Queue<Integer> queue = new LinkedList<>();
        
        queue.add(start);
        visited[start] = true;
        dist[start] = 0;
        pre[start] = -1;
        while (!queue.isEmpty()) {
            int u = queue.poll();
            for (int v : graph.get(u))
                if (!visited[v]) {
                    dist[v] = dist[u] + 1;
                    queue.add(v);
                    visited[start] = true;
                    pre[v] = u;
                }
        }
    }
}

Solution1.b Code:

class Solution {
    
    class Pack {
        public int far_id;
        public int max_len;
        
        Pack() {
            far_id = -1;
            max_len = 0;
        }
    }

    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        if(edges == null || edges.length == 0) {
            if(n == 1) return Arrays.asList(0);
            else return new ArrayList<>();
        }
        
        // graph
        List<List<Integer>> graph = new ArrayList<>();
        for(int i = 0; i < n; i++) {
            graph.add(new ArrayList<>());
        }
        for(int i = 0; i < edges.length; i++) {
            graph.get(edges[i][0]).add(edges[i][1]);
            graph.get(edges[i][1]).add(edges[i][0]);
        }
        
        int[] pre = new int[n];
        Arrays.fill(pre, -1);
        
        Pack pack1 = dfs(graph, edges[0][0], -1, pre);
        Pack pack2 = dfs(graph, pack1.far_id, -1, pre);
        
        List<Integer> list = new ArrayList<>();
        
        pack1.max_len -= 1;
        pack2.max_len -= 1;

        int v = pack2.far_id;
        while (v != -1) {
            System.out.println("v: " + v);
            list.add(v);
            v = pre[v];
        }
        
        // prepare result
        if(list.size() % 2 == 1) {
            return Arrays.asList(list.get(list.size() / 2));
        }
        else {
            return Arrays.asList(list.get(list.size() / 2 - 1), list.get(list.size() / 2));
        }
    }
    
    
    private Pack dfs(List<List<Integer>> graph, int start_id, int prev, int[] pre) {
        Pack cur_pack = new Pack();
        if(graph.get(start_id).size() == 1) {
            // leaf
            cur_pack.far_id = start_id;
        }
        pre[start_id] = prev;
        
        List<Integer> next_list = graph.get(start_id);
        for(int next: next_list) {
            if(next == prev) continue;
            Pack pack = dfs(graph, next, start_id, pre);
            if(pack.max_len > cur_pack.max_len) {
                cur_pack.max_len = pack.max_len;
                cur_pack.far_id = pack.far_id;
            }
        }
        
        cur_pack.max_len++;
        return cur_pack;
    }
}

Solution2 Code:

public class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        if (n == 1) return Arrays.asList(0);
        // if (n == 2) return Arrays.asList(0, 1);
        
        // graph: adj_list init
        List<List<Integer>> adj = new ArrayList<List<Integer>>();
        for(int i = 0; i < n; i++) {
            adj.add(new ArrayList<Integer>()); 
        }
        for (int[] edge : edges) {
            adj.get(edge[0]).add(edge[1]);
            adj.get(edge[1]).add(edge[0]);
        }
        

        Queue<Integer> leaves = new LinkedList<>();
        for (int i = 0; i < n; ++i) {
            if (adj.get(i).size() == 1) leaves.offer(i);
        }
        
        while (n > 2) {
            int num_leaves = leaves.size();
            n -= num_leaves;
            for(int i = 0; i < num_leaves; i++) {
                int u = leaves.poll();
                int v = adj.get(u).iterator().next();
                adj.get(v).remove(new Integer(u)); // remove by finding value.
                if (adj.get(v).size() == 1) leaves.add(v);
            }
        }
        return (List)leaves;
    }

}

Solution3 Code:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容