1. 程式人生 > >LeetCode:310. Minimum Height Trees(Week 5)

LeetCode:310. Minimum Height Trees(Week 5)

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

    1. 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).

    2. 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 :

      Input: n = 4, edges = [[1, 0], [1, 2], [1, 3]]
      
              0
              |
              1
             / \
            2   3 
      
      Output: [1]
      
    • Example 2 :

      Input: n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
      
           0  1  2
            \ | /
              3
              |
              4
              |
              5 
      
      Output: [3, 4]
      
    • Note:

      • According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.”
      • The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
  • 解題思路

    • 解法1 - 暴力破解(超時Failed)

      • 步驟
        • 以每個節點都為根,對每個節點使用BFS遍歷得到以該節點為根時樹的高度
        • 比較各個節點時樹的高度,得到Minimum Height Trees的根節點
      • 程式碼
        class Solution {
        public:
            vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
        	vector<int> v;
        	int edgesArray[n][n];
        
                int height[n];
        
                for(int i = 0; i < n; ++i) {
                    height[i] = getHeight(n, i, edges);
                }
        
                // 尋找最小高度樹的根節點
                int min = n;
        	for(int i = 0; i < n; ++i) {
        		if(height[i] < min) {
                    min = height[i];
        			v.clear();
        			v.push_back(i);
        		}
        		else if(height[i] == min) {
        			v.push_back(i);
        		}
        	}
        	return v;
            }
        
            // bfs遍歷
            int getHeight(int n, int root, vector<pair<int, int> >& edges) {
            	int h = 0;
                int visited[n] = {0};
                visited[root] = 1;
            	queue<int> q;
            	q.push(root);
            	int depth[n] = {0};
            	while(!q.empty()) {
        		int tmp = q.front();
        		q.pop();
        		for(auto iter = edges.begin(); iter != edges.end(); iter++) {
           		if((*iter).first == tmp || (*iter).second == tmp) {
           			int newNode = (*iter).first + (*iter).second - tmp;
        	                if(!visited[newNode]) {
        	                        depth[newNode] = depth[tmp] + 1;
        	                        q.push(newNode);
        	                        visited[newNode] = 1;
        	                }
        	    	}
        	   }
            	}
        	int max = 0;
            	for(int i = 0; i < n; ++i) {
            		if(depth[i] > max)
            			max = depth[i];
            	}
            	return max;
            }
        };
        
      • 複雜度 – O ( V 2 E ) O(V^2E) (V表示節點數量,E表示邊的數量)
      • 結果果然超時了
        在這裡插入圖片描述
      • 改進與優化
        • 可以利用map資料結構,來儲存每個節點對應其連線的節點所組成的一個對映map<int, vector<int> >,或者一個二維陣列
        • 複雜度 – O ( V E ) O(VE) (V表示節點數量,E表示邊的數量)
        • 優化的程式碼
          class Solution {
          public:
              vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
          	vector<int> v;
          	map<int, vector<int> > myMap;
          
                  int height[n];
          
                  for(auto iter = edges.begin(); iter != edges.end(); iter++) {
                      myMap[(*iter).first].push_back((*iter).second);
                      myMap[(*iter).second].push_back((*iter).first);
                  }
          
                  for(int i = 0; i < n; ++i) {
                      height[i] = getHeight(n, i, myMap);
                  }
          
                  // 尋找最小高度樹的根節點
                  int min = n;
          	for(int i = 0; i < n; ++i) {
          		if(height[i] < min) {
                      		min = height[i];
          			v.clear();
          			v.push_back(i);
          		}
          		else if(height[i] == min) {
          			v.push_back(i);
          		}
          	}
          	return v;
          }
          
              // bfs遍歷
              int getHeight(int n, int root, map<int, vector<int> > myMap) {
                  int h = 0;
                  int visited[n] = {0};
                  visited[root] = 1;
                  queue<int> q;
                  q.push(root);
                  int depth[n] = {0};
                  while(!q.empty()) {
                      int tmp = q.front();
                      q.pop();
                      for(auto iter = myMap[tmp].begin(); iter != myMap[tmp].end(); iter++) {
                          if(!visited[(*iter)]) {
                              depth[(*iter)] = depth[tmp] + 1;
                              q.push((*iter));
                              visited[(*iter)] = 1;
                          }
                      }
                  }
                  int max = 0;
                  for(int i = 0; i < n; ++i) {
                      if(depth[i] > max)
                          max = depth[i];
                  }
                  return max;
              }
          };
          
        • 但是依舊超時
          在這裡插入圖片描述
    • 思考提示:How many MHTs can a graph have at most?

      • 每個樹最多有兩個MHTs。假如有三個或多個可以作為MHTs的根節點,根據樹的定義,每兩個節點都可以找到一條路徑連線起來,樹是一個強連通的圖,不存在簡單環,則至少一個一個或多個比其餘節點高,則大於三個MHTs不成立。
    • 解法2 – 類剝洋蔥求解(AC)

      • 本方法學習自其他優秀解法
      • 步驟
        1. 建立一個對映表,記錄每一個點與其直接相連的點
        2. 將樹的葉節點(度為1的節點)加入一個佇列中
        3. 假如當前佇列中儲存的節點數小於等於2,則退出迴圈。否則遍歷佇列中的每個節點。對每個節點,將其彈出佇列,同時將與其相連節點的集合中將該節點刪去,如果刪完該節點後此節點也變成一個葉節點(度數為1),那麼將這個節點加入佇列中。
      • 時間複雜度 – O ( n ) O(n)
      • 實現程式碼
        class Solution {
        public:
            vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
                if(n == 1) return {0};
                vector<int> ans;
                vector<set<int> > myVec(n);
                for(auto edge : edges) {
                    myVec[edge.first].insert(edge.second);
                    myVec[edge.second].insert(edge.first);
                }
                queue<int> q;
                for(int i = 0; i < n; ++i)
                    if(myVec[i].size() == 1)
                        q.push(i);
        
                while(n > 2) {
                    int size = q.size();
                    n -= size;
                    for(int i = 0; i < size; ++i) {
                        int tmp = q.front();
                        q.pop();
                        for (auto it : myVec[tmp]) {
                            myVec[it].erase(tmp);
                            if (myVec[it].size() == 1) q.push(it);
                        }
                    }
                }
        
                while(!q.empty()) {
                    ans.push_back(q.front());
                    q.pop();
                }
        
                return ans;
            }
        };