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]

解:

https://discuss.leetcode.com/topic/30572/share-some-thoughts
中详细说明了思路,复制粘贴如下:

First let’s review some statement for tree in graph theory:

(1) A tree is an undirected graph in which any two vertices are
connected by exactly one path.

(2) Any connected graph who has n nodes with n-1 edges is a tree.

(3) The degree of a vertex of a graph is the number of
edges incident to the vertex.

(4) A leaf is a vertex of degree 1. An internal vertex is a vertex of
degree at least 2.

(5) A path graph is a tree with two or more vertices that is not
branched at all.

(6) A tree is called a rooted tree if one vertex has been designated
the root.

(7) The height of a rooted tree is the number of edges on the longest
downward path between root and a leaf.
OK. Let’s stop here and look at our problem.

Our problem want us to find the minimum height trees and return their root labels. First we can think about a simple case – a path graph.

For a path graph of n nodes, find the minimum height trees is trivial. Just designate the middle point(s) as roots.

Despite its triviality, let design a algorithm to find them.

Suppose we don’t know n, nor do we have random access of the nodes. We have to traversal. It is very easy to get the idea of two pointers. One from each end and move at the same speed. When they meet or they are one step away, (depends on the parity of n), we have the roots we want.

This gives us a lot of useful ideas to crack our real problem.

For a tree we can do some thing similar. 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!

The time complexity and space complexity are both O(n).

Note that for a tree we always have V = n, E = n-1.

解法二:

Tree DP

Alternatively, one can solve this problem directly by tree dp.
Let dp[i] be the height of the tree when the tree root is i.
We compute dp[0] … dp[n - 1] by tree dp in a dfs manner.

Arbitrarily pick a node, say node 0, as the root, and do a dfs.
When we reach a node u, and let T be the subtree by removing all u’s descendant (see the right figure below).
We maintain a variable acc that keeps track of the length of the longest path in T with one endpoint being u.
Then dp[u] = max(height[u], acc)
Note, acc is 0 for the root of the tree.

        |                 |
        .                 .
       /|\               /|\
      * u *             * u *
       /|\
      / | \
     *  v  *

. denotes a single node, and * denotes a subtree (possibly empty).

Now it remains to calculate the new acc for any of u’s child, v.
It is easy to see that the new acc is the max of the following

acc + 1 — extend the previous path by edge uv;
max(height[v’] + 2), where v != v’ — see below for an example.
这里写图片描述
In fact, the second case can be computed in O(1) time instead of spending a time proportional to the degree of u.
Otherwise, the runtime can be quadratic when the degree of some node is Omega(n).
The trick here is to maintain two heights of each node, the largest height (the conventional height), and the second largest height
(the height of the node after removing the branch w.r.t. the largest height).

Therefore, after the dfs, all dp[i]’s are computed, and the problem can be answered trivially.
The total runtime is still O(n)

解法三:

对任意一条边a-b,求出a到b方向的最大深度和b到a方向的最大深度,用记忆化搜索的方法加快运算,最后遍历一边树的节点,根据求出的『边的深度』就能轻易求出以当前节点为根的树的高度


class edge
{
    public:
        pair<int,int> e;
        int len_f;
        int len_r;
        edge(const pair<int,int> &p):e(p),len_f(-1),len_r(-1){}
        edge(const pair<int,int> &p,int i,int j):e(p),len_f(i),len_r(j){}
};


class Solution {
public:
    map<int,vector<int>>tree;
    vector<edge> edges_arr;
    int calc_height(int root,int parent)
    {
        int m_max=0;
        for(auto &e:tree[root] )
        {
            if(min(root,parent)==edges_arr[e].e.first&&max(root,parent)==edges_arr[e].e.second)continue;
            if(root==edges_arr[e].e.first)
            {
                if(edges_arr[e].len_f<0)
                {
                    int tmp=calc_height(edges_arr[e].e.second,edges_arr[e].e.first);
                    edges_arr[e].len_f=tmp;

                }
                if(edges_arr[e].len_f>m_max)m_max=edges_arr[e].len_f;

            }
            else
            {
                if(edges_arr[e].len_r<0)
                {
                    int tmp=calc_height(edges_arr[e].e.first,edges_arr[e].e.second);
                    edges_arr[e].len_r=tmp;
                }
                if(edges_arr[e].len_r>m_max)m_max=edges_arr[e].len_r;
            }
        }
        ++m_max;
        return m_max;

    }
    vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
        for(auto &elem:edges)
        {
            if(elem.first>elem.second)swap(elem.first,elem.second);
            edges_arr.push_back(edge(elem));
            tree[elem.first].push_back(edges_arr.size()-1);
            tree[elem.second].push_back(edges_arr.size()-1);
        }
        for(auto& i:edges_arr)
        {
            if(i.len_f<0)
            {
            int res=calc_height(i.e.second,i.e.first);
            i.len_f=res;
            }

            if(i.len_r<0)
            {
            int res=calc_height(i.e.first,i.e.second);
            i.len_r=res;
            }
        }



        vector<int>result;
        for(int i=0;i<n;++i)
        {
            int height=0;
            for(auto& e:tree[i])
            {
                if (edges_arr[e].e.first==i)
                {
                    height=max(height,edges_arr[e].len_f);
                }
                else
                {
                    height=max(height,edges_arr[e].len_r);
                }
            }
            result.push_back(height);
        }
        pprintv(result);
        vector<int>ret;
        //find min
        int mm=result[0];
        for(int i=1;i<n;++i)
        {
            if(result[i]<mm)mm=result[i];
        }
        for(int i=0;i<n;++i)
        {
            if(result[i]==mm)ret.push_back(i);
        }
        return ret;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值