Given a list of Connections, which is the Connection class (the city name at both ends of the edge and a cost between them), find edges that can connect all the cities and spend the least amount.
Return the connects if can connect all the cities, otherwise return empty list.
思路
最小生成树问题
用kruskal算法,比较好理解,就是贪心算法,越短的边越要选。但是如果k个点其实只要k-1条边,如果两个点已经在一个图里就不需要边给它连上了,所以这样的边就不要。
- 所有边按照权重排序,从小到大选择,如果选择的这个边的两端,已经在一个图里了,那就不要它,否则就要它。
但是怎么判断两个节点是否在一个图里呢?使用union-find(并查集)的方法。使用一个字典来记录每一个节点的爸爸。
- 同一个图里的节点有一个共同的祖先作为他们的代表,想知道两个节点是不是在一个图里,就看他们的祖先是不是在一个图里。
- 如果一个点的爸爸就是自己,说明他就是他这张图里的祖先了
- 当所有点都还是孤立的时候,他们的爸爸就是他们自己。
- 当AB两个点被连接起来了,那A家和B家就成一家了,就得把他们合并起来,具体做法就是把A点的祖先[老A]指向B点的祖先[老B]就好了。这样一来[老B]就是[老A]的爸爸,原来把[老A]当作祖先的人就会把[老B]当作祖先。
- 这样做已经可以判断了。但是一直要递归的网上走会比较浪费时间,可以在每次调用find方法找祖先以后,直接把祖先当成爸爸,这样路径就短了。
代码
这个代码在beats 94.00% Submissions in lintcode。命名命得不咋地。
class Solution:
# @param {Connection[]} connections given a list of connections
# include two cities and cost
# @return {Connection[]} a list of connections from results
def lowestCost(self, connections):
# Write your code here
connections.sort(key=lambda c:(c.cost,c.city1,c.city2))
ans = []
group = {}
def find(city):
while group.get(city) != city:
city = group.get(city)
return city
cityset = set()
for con in connections:
cityset.add(con.city1)
cityset.add(con.city2)
for con in connections:
c1 = con.city1
c2 = con.city2
if c1 not in group.keys():
group[c1] = c1
if c2 not in group.keys():
group[c2] = c2
g1 = find(c1)
g2 = find(c2)
group[c1] = g1
group[c2] = g2
if g1 != g2:
ans.append(con)
group[g2] = g1
if len(cityset) - 1 == len(ans):
return ans
return []