您的位置:首页 > 其它

算法分析与设计第三周习题:图论算法之310. Minimum Height Trees

2017-03-09 22:27 447 查看
今天刚上完算法课,课上知识内容包括拓扑排序和图论一些基本知识,所以我就去leetcode上面找了一道有关图论的题目来巩固自己所学的知识。

题目简介:

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.

提炼内容:

从一个无向图中寻找一个根节点,使得该根节点生成的树拥有最小的高度。

显然,如果对每个节点使用DFS或者BFS去搜索该节点的树高度将会得到

O(V(V+E))的时间,这是一个十分慢的搜索过程。

因此,我试着换个角度来思考这个问题。如果一条根到叶子的路径上包含a,b,c,d,e,f,g七个点,那么当我们选取中间点d作为根时,d到所有节点的路径是最短的(相比b到g或者a到f)。也就是说,我们可以从图中逐个删除度为1的节点,那么当图中最后只剩一条边或者无边的时候,即可获得中间节点,也就是生成树高度最小的根节点。

第一次尝试:

不使用邻接表保存边,不使用列表保存度为1的叶子节点后所产生的新节点。测试结果,只通过62个测例,当节点数达到2020时,算法超时。

#找度为1的点,根据其边来减小点的度数,然后删除该点
#重复该步骤直至无点度数大于1
while 1:
hasDegreeMoreThan1 = False
oneDegreeNodeList = []
for i in range(n):
if degree[i][1] == False:
if degree[i][0] == 1:
oneDegreeNodeList.append(i)
#只剩一个点的情况
elif degree[i][0] == 0:
return [i]
else:
hasDegreeMoreThan1 = True
if hasDegreeMoreThan1:
#如果有度数大于1的点,则根据入度为1的点的边减少相应点的度数
for i in range(len(oneDegreeNodeList)):
for j in range(len(edges)):
#访问完毕便删除度数等于1的点的边
if edges[j][0] == oneDegreeNodeList[i]:
degree[edges[j][1]][0] -= 1
del edges[j]
break
elif edges[j][1] == oneDegreeNodeList[i]:
degree[edges[j][0]][0] -= 1
del edges[j]
break

#标记度数为1的点已被删除
degree[oneDegreeNodeList[i]][1] = True
else:
return oneDegreeNodeList


第二次尝试:

使用列表保存新叶子,当测例节点数达到5000时超时。

#重复该步骤直至无点度数大于1
while size - usedEdgeCounts > 1:
#用来保存新的度数为1的点
newOneDegList = []
#如果有度数大于1的点,则根据入度为1的点的边减少相应点的度数
for i in range(len(oneDegreeNodeList)):
for j in range(len(edges)):
#访问完毕便删除度数等于1的点的边
if edges[j][0] == oneDegreeNodeList[i]:
degree[edges[j][1]] -= 1
if degree[edges[j][1]] == 1:
newOneDegList.append(edges[j][1])
#若有度数为0的点即可直接返回
elif degree[edges[j][1]] == 0:
return [edges[j][1]]
del edges[j]
usedEdgeCounts += 1
break
elif edges[j][1] == oneDegreeNodeList[i]:
degree[edges[j][0]] -= 1
if degree[edges[j][0]] == 1:
newOneDegList.append(edges[j][0])
elif degree[edges[j][0]] == 0:
return [edges[j][0]]
del edges[j]
usedEdgeCounts += 1
break
oneDegreeNodeList = newOneDegList
return oneDegreeNodeList


第三次尝试:

参考了leetcode上其他用户所使用的数据结构,改用邻接表保存边(而不是每次都遍历edges)且使用列表保存新叶子。AC

#度为1的点必为叶子节点,若以度为1的点作为根,则生成树高度最高
#算法思想:从图中逐个删除入度为1的点,直至只剩下入度为1的点即为高度最小的根。

class Solution(object):
def findMinHeightTrees(self, n, edges):
#无顶点则直接返回空
if n == 0:
return []
#无边则返回各个定点
if edges == []:
return [x for x in range(n)]

#统计边个数并用邻接表将边存储起来
size = len(edges)
adjList = [set() for _ in range(n)]
for i, j in edges:
adjList[i].add(j)
adjList[j].add(i)

leaves = []
for i in range(len(adjList)):
if len(adjList[i]) == 1:
leaves.append(i)

while size > 1:
newLeaves = []
for leaf in leaves:
node = adjList[leaf].pop()
adjList[node].remove(leaf)
if len(adjList[node]) == 1:
newLeaves.append(node)
elif len(adjList[node]) == 0:
return [node]
size -= len(leaves)
leaves = newLeaves
return leaves


至此,该题代码编写完毕。累。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法