对于图求最小生成树
Prim算法中,有两个顶点集合:v和u,一个边的集合:e
v:e中的顶点
u:all - v
e:存放所有已选的边
算法描述:
从所有边中取最短的边e(i,j),i属于v,j属于u。
将e(i,j)放入e中
将j从u中放入v中
在写算法的过程中,增加了数据结构waiting_edge,存放所有(v,u)点对
def prim(graph):
v = set() # chosen vertex
e = [] # edge(i,j) list
waiting_edge = defaultdict(list) # len:edge(i,j) list
# init
v.add(0)
for j, weight in enumerate(graph[0]):
if weight:
waiting_edge[weight].append((0,j))
# loop1
while waiting_edge:
find = None
# loop2
while waiting_edge and find is None:
min_weight = min(waiting_edge)
del_list = []
# loop3
for (i_i, i_j) in waiting_edge[min_weight]:
del_list.append((i_i, i_j))
if i_j not in v:
find = i_j
e.append((i_i, i_j))
break
# loop4
for item in del_list:
waiting_edge[min_weight].remove(item)
if not waiting_edge[min_weight]:
del waiting_edge[min_weight]
if find:
# add i to v
# expand waiting_edge
v.add(find)
# loop5
for j, weight in enumerate(graph[find]):
if weight and j not in v:
waiting_edge[weight].append((find,j))
return e
这是最直观的解法,直接翻译了上面的算法。
loop1至多遍历Vertex次,也就是说,最小生成树不可能含有多于Vertex个点
loop2先不管,先看loop3和loop4。它们循环的次数相同,至多循环Edge次。注意loop3中有一个查询set的语句,意味着log(Vertex)的时间开销。loop4中有remove的操作,最坏情况下O(Edge)的开销。
loop2里有一个min操作,根据waiting_edge的数据结构,可能为O(Edge)[hash]次比较,也可能为O(1)[堆]。
loop2里有跟新waiting_edge的操作,可能为O(1)[hash]也可能为O(log(Edge))。
loop5可能遍历Vertex次,如果是用图的数据结构的话总共遍历Edge次
可以看出还是很耗时的。
下面来挖掘隐含条件:
1. 不需要存储所有的边:生成树只会有Vertex-1条边
2. 如果i和j都在v中,且k在u中,那么只需要存储e(i,k)和e(j,k)中较短的边。这里有一个loose操作(参见Dijkastra算法)。
所以需要一个数据结构edge_info
edge_info[u中的节点] = (较短的边长, v中的节点)
edge_info[v中的节点]=无意义的数,这里我用chosen_int表示
max_int = 0xffffff
chosen_int = max_int - 1
def prim_pro(graph):
edge_info = [(weight, 0) for weight in graph[0]]
edge_info[0] = (chosen_int, 0)
e = [] # edge(i,j) list
for i in xrange(1, len(graph)):
# find min
min_info = min(edge_info)
# put k in v, reset min_info[k]
k = edge_info.index(min_info)
e.append((min_info[1], k))
edge_info[k] = (chosen_int, min_info[1])
# loose
for j, weight in enumerate(graph[k]):
if weight < edge_info[j][0] and edge_info[j][0] != chosen_int:
edge_info[j] = (weight, k)
return e
带上隐含条件后的代码就简单多了
耗时为O(Vertex^2)