题目

https://leetcode.com/problems/minimum-height-trees/

题解

方法1:图的邻接矩阵 DFS(超时)

我一想,这不就是个图嘛,于是随手敲出一个 DFS,结果。。超时了。

class Vertex {Integer val;List<Integer> next;public Vertex() {next = new LinkedList<>();}
}class Solution {public List<Integer> findMinHeightTrees(int n, int[][] edges) {Vertex[] v = new Vertex[n];for (int i = 0; i < n; i++) {v[i] = new Vertex();}for (int i = 0; i < edges.length; i++) {v[edges[i][0]].next.add(edges[i][1]);v[edges[i][1]].next.add(edges[i][0]);}// dfsint globalMin = n;ArrayList<Integer> result = new ArrayList<>();boolean[] visited = new boolean[n];for (int i = 0; i < n; i++) {visited[i] = true; // 避免原地转圈(只记录当前路径走过的节点)dfs(v, visited, i, 0);if (curMax < globalMin) {result.clear();result.add(i);globalMin = curMax;} else if (curMax == globalMin) {result.add(i);}curMax = 0;visited[i] = false; // 恢复原状,下次再用}return result;}int curMax = 0;public void dfs(Vertex[] v, boolean[] visited, int i, int depth) {curMax = Math.max(curMax, depth);for (Integer next : v[i].next) {if (!visited[next]) {visited[next] = true;dfs(v, visited, next, depth + 1);visited[next] = false;}}}
}

方法2:拓扑排序(Topological Sorting)

参考:https://leetcode.com/problems/minimum-height-trees/solution/

Intuition

First of all, let us clarify some concepts.

The distance between two nodes is the number of edges that connect the two nodes.

Note, normally there could be multiple paths to connect nodes in a graph. In our case though, since the input graph can form a tree from any node, as specified in the problem, there could only be one path between any two nodes. In addition, there would be no cycle in the graph. As a result, there would be no ambiguity in the above definition of distance.

The height of a tree can be defined as the maximum distance between the root and all its leaf nodes.

With the above definitions, we can rephrase the problem as finding out the nodes that are overall close to all other nodes, especially the leaf nodes.

If we view the graph as an area of circle, and the leaf nodes as the peripheral of the circle, then what we are looking for are actually the centroids of the circle, i.e. nodes that is close to all the peripheral nodes (leaf nodes).

For instance, in the above graph, it is clear that the node with the value 1 is the centroid of the graph. If we pick the node 1 as the root to form a tree, we would obtain a tree with the minimum height, compared to other trees that are formed with any other nodes.

Before we proceed, here we make one assertion which is essential to the algorithm.

For the tree-alike graph, the number of centroids is no more than 2.

If the nodes form a chain, it is intuitive to see that the above statement holds, which can be broken into the following two cases:

  • If the number of nodes is even, then there would be two centroids.
  • If the number of nodes is odd, then there would be only one centroid.

For the rest of cases, we could prove by contradiction. Suppose that we have 3 centroids in the graph, if we remove all the non-centroid nodes in the graph, then the 3 centroids nodes must form a triangle shape, as follows:

Because these centroids are equally important to each other, and they should equally close to each other as well. If any of the edges that is missing from the triangle, then the 3 centroids would be reduced down to a single centroid.

However, the triangle shape forms a cycle which is contradicted to the condition that there is no cycle in our tree-alike graph. Similarly, for any of the cases that have more than 2 centroids, they must form a cycle among the centroids, which is contradicted to our condition.

Therefore, there cannot be more than 2 centroids in a tree-alike graph.

Algorithm

Given the above intuition, the problem is now reduced down to looking for all the centroid nodes in a tree-alike graph, which in addition are no more than two.

The idea is that we trim out the leaf nodes layer by layer, until we reach the core of the graph, which are the centroids nodes.

Once we trim out the first layer of the leaf nodes (nodes that have only one connection), some of the non-leaf nodes would become leaf nodes.

The trimming process continues until there are only two nodes left in the graph, which are the centroids that we are looking for.

The above algorithm resembles the topological sorting algorithm which generates the order of objects based on their dependencies. For instance, in the scenario of course scheduling, the courses that have the least dependency would appear first in the order.

In our case, we trim out the leaf nodes first, which are the farther away from the centroids. At each step, the nodes we trim out are closer to the centroids than the nodes in the previous step. At the end, the trimming process terminates at the centroids nodes.

Implementation

Given the above algorithm, we could implement it via the Breadth First Search (BFS) strategy, to trim the leaf nodes layer by layer (i.e. level by level).

  • Initially, we would build a graph with the adjacency list from the input.
  • We then create a queue which would be used to hold the leaf nodes.
  • At the beginning, we put all the current leaf nodes into the queue.
  • We then run a loop until there is only two nodes left in the graph.
  • At each iteration, we remove the current leaf nodes from the queue. While removing the nodes, we also remove the edges that are linked to the nodes. As a consequence, some of the non-leaf nodes would become leaf nodes. And these are the nodes that would be trimmed out in the next iteration.
  • The iteration terminates when there are no more than two nodes left in the graph, which are the desired centroids nodes.

Here are some sample implementations that are inspired from the post of dietpepsi in the discussion forum.

class Vertex {Set<Integer> neighbors;public Vertex() {neighbors = new HashSet<Integer>();}
}class Solution {public List<Integer> findMinHeightTrees(int n, int[][] edges) {Vertex[] v = new Vertex[n];for (int i = 0; i < n; i++) {v[i] = new Vertex();}// 初始化邻接矩阵for (int[] edge : edges) {v[edge[0]].neighbors.add(edge[1]);v[edge[1]].neighbors.add(edge[0]);}// 逐层删掉最外层叶子,类似拓扑排序int remain = n;List<Integer> leaves = new ArrayList<>();for (int i = 0; i < v.length; i++) {if (v[i].neighbors.size() == 1) leaves.add(i);}while (remain > 2) { // 最终只有可能剩余2个或1个remain -= leaves.size();List<Integer> newLeaves = new ArrayList<>();for (int i : leaves) {int nbr = v[i].neighbors.iterator().next(); // 唯一的neighborv[nbr].neighbors.remove(i); // 要删除v[i],就要删除它的所有neighbor节点对v[i]的记录if (v[nbr].neighbors.size() == 1)newLeaves.add(nbr);}leaves = newLeaves;}if (leaves.size() == 0) leaves.add(0);return leaves;}
}

leetcode 310. Minimum Height Trees | 310. 最小高度树(图的邻接矩阵DFS / 拓扑排序)相关推荐

  1. 310. Minimum Height Trees 【Medium】 树

    题目:给出一个树,求出所有作为根节点时树的高度最小的节点 思路:用剪枝的办法,每次剪掉叶子节点,最终剩下一个或者两个节点时结束. 原理:一个节点的树的高度为该节点到最远的节点的距离,且此最远节点必为叶 ...

  2. 310. Minimum Height Trees

    输入:包含n个节点的无向图.n:表示从0到n-1,n个节点.edges:int数组,是从一个节点到另外一个节点.但是没有方向. 输出:以哪些节点为根节点,具有最小高度的树,返回这些根节点. 规则:一个 ...

  3. leetcode 547. Number of Provinces | 547. 省份数量(图的邻接矩阵 DFS)

    题目 https://leetcode.com/problems/number-of-provinces/ 题解 本题用了图的邻接矩阵 DFS.另外,也可以用并查集来做. class Solution ...

  4. LeetCode 310. Minimum Height Trees

    文章目录 知识点 结果 菜鸡的DFS+记忆化 网友的BFS"剥洋葱" 实现 菜鸡的DFS+记忆化 代码 反思 网友的BFS"剥洋葱" 代码 反思 知识点 图的遍 ...

  5. [Leetcode][第329题][JAVA][矩阵中的最长递增路径][DFS][拓扑排序]

    [问题描述][中等] [解答思路] 1. 记忆化深度优先搜索 复杂度 class Solution {public int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, ...

  6. 树的最小高度 Minimum Height Trees

    2019独角兽企业重金招聘Python工程师标准>>> 问题: For a undirected graph with tree characteristics, we can ch ...

  7. LeetCode 310. 最小高度树(图 聪明的BFS,从外向内包围)

    文章目录 1. 题目 2. 解题 2.1 暴力BFS 2.2 聪明的BFS 1. 题目 对于一个具有树特征的无向图,我们可选择任何一个节点作为根.图因此可以成为树,在所有可能的树中,具有最小高度的树被 ...

  8. LeetCode 310 最小高度树

    题目描述 树是一个无向图,其中任何两个顶点只通过一条路径连接. 换句话说,一个任何没有简单环路的连通图都是一 棵树.给你一棵包含 n 个节点的数,标记为 0 到 n - 1 .给定数字 n 和一个有 ...

  9. LeetCode 310. 最小高度树(广度优先遍历)

    题目描述 对于一个具有树特征的无向图,我们可选择任何一个节点作为根.图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树.给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的 ...

最新文章

  1. openwrt+linux编译,openwrt x86 编译部署
  2. Android创建自己的gradle依赖包
  3. B2B 企业如何高效获客增长?
  4. c语言实参形参函数调用指针引用 符号实例,C语言实参、形参、函数调用、指针、引用、符号实例.doc...
  5. svchost.exe启动服务原理
  6. linux无filelength函数,Linux Shell 自定义函数(定义、返回值、变量作用域)介绍
  7. phpmyadmin4.8.1远程文件包含漏洞
  8. linux镜像下载和vmware虚拟主机部署
  9. 病毒周报(080630至080706)
  10. 0基础参加数学建模,最大程度冲击奖项
  11. 大神之光照耀着我 - 我的成长之路 - 起点
  12. win11提示找不到gpedit.msc命令
  13. 经典计算机书籍-自制系列
  14. Context-Aware Zero-Shot Recognition 论文翻译
  15. 蓝牙4.0BLE 手机控制 cc2540 CC2541 的串口透传功能已实现
  16. SpringCloudAlibaba nacos学习笔记
  17. 蓝牙耳机哪款好用?2020高性能低延迟游戏蓝牙耳机推荐
  18. Mybatis配置驼峰影射作用
  19. Cortex-M3/M4学习随笔——一些处理器基本信息
  20. 物联网-鸡舍项目总结

热门文章

  1. HDU - 1757 A Simple Math Problem(矩阵快速幂,水题)
  2. 广度优先遍历算法-02合法的括号问题
  3. Miller-Rabin素数测试
  4. 最简单的基于FFmpeg的AVDevice例子(读取摄像头)
  5. 学习windows 应用层 inline hook 原理总结
  6. _MSC_VER详细介绍
  7. C/C++中容易造成内存溢出的函数
  8. Flink 架构:三层架构体系、运行时组件
  9. go build -X 的妙用
  10. C和C++中static的用法及友元