class Edge(object):
"""边"""
def __init__(self, a, b, weight):
self.a = a # 第一个顶点
self.b = b # 第二个顶点
self.weight = weight # 权值
def v(self):
return self.a
def w(self):
return self.b
def wt(self):
return self.weight
def other(self, x):
# 返回x顶点连接的另一个顶点
if x == self.a or x == self.b:
if x == self.a:
return self.b
else:
return self.a
def __lt__(self, other):
# 小于号重载
return self.weight < other.wt()
def __le__(self, other):
# 小于等于号重载
return self.weight <= other.wt()
def __gt__(self, other):
# 大于号重载
return self.weight > other.wt()
def __ge__(self, other):
# 大于等于号重载
return self.weight >= other.wt()
def __eq__(self, other):
# ==号重载
return self.weight == other.wt()
class DenseGraph(object):
"""有权稠密图 - 邻接矩阵"""
def __init__(self, n, directed):
self.n = n # 图中的点数
self.m = 0 # 图中的边数
self.directed = directed # bool值,表示是否为有向图
self.g = [[None for _ in range(n)] for _ in range(n)] # 矩阵初始化都为None的二维矩阵
def V(self):
# 返回图中点数
return self.n
def E(self):
# 返回图中边数
return self.m
def addEdge(self, v, w, weight):
# v和w中增加一条边,v和w都是[0,n-1]区间
if v >= 0 and v < n and w >= 0 and w < n:
if self.hasEdge(v, w):
self.m -= 1
self.g[v][w] = Edge(v, w, weight)
if not self.directed:
self.g[w][v] = Edge(w, v, weight)
self.m += 1
def hasEdge(self, v, w):
# v和w之间是否有边,v和w都是[0,n-1]区间
if v >= 0 and v < n and w >= 0 and w < n:
return self.g[v][w] != None
class adjIterator(object):
"""相邻节点迭代器"""
def __init__(self, graph, v):
self.G = graph # 需要遍历的图
self.v = v # 遍历v节点相邻的边
self.index = 0 # 遍历的索引
def __iter__(self):
return self
def next(self):
while self.index < self.G.V():
# 当索引小于节点数量时遍历,否则为遍历完成,停止迭代
if self.G.g[self.v][self.index]:
r = self.G.g[self.v][self.index]
self.index += 1
return r
self.index += 1
raise StopIteration()
class SparseGraph(object):
"""有权稀疏图- 邻接表"""
def __init__(self, n, directed):
self.n = n # 图中的点数
self.m = 0 # 图中的边数
self.directed = directed # bool值,表示是否为有向图
self.g = [[] for _ in range(n)] # 矩阵初始化都为空的二维矩阵
def V(self):
# 返回图中点数
return self.n
def E(self):
# 返回图中边数
return self.m
def addEdge(self, v, w, weight):
# v和w中增加一条边,v和w都是[0,n-1]区间
if v >= 0 and v < n and w >= 0 and w < n:
# 考虑到平行边会让时间复杂度变为最差为O(n)
# if self.hasEdge(v, w):
# return None
self.g[v].append(Edge(v, w, weight))
if v != w and not self.directed:
self.g[w].append(Edge(w, v, weight))
self.m += 1
def hasEdge(self, v, w):
# v和w之间是否有边,v和w都是[0,n-1]区间
# 时间复杂度最差为O(n)
if v >= 0 and v < n and w >= 0 and w < n:
for i in self.g[v]:
if i.other(v) == w:
return True
return False
class adjIterator(object):
"""相邻节点迭代器"""
def __init__(self, graph, v):
self.G = graph # 需要遍历的图
self.v = v # 遍历v节点相邻的边
self.index = 0 # 遍历的索引
def __iter__(self):
return self
def next(self):
if len(self.G.g[self.v]):
# v有相邻节点才遍历
if self.index < len(self.G.g[self.v]):
r = self.G.g[self.v][self.index]
self.index += 1
return r
else:
raise StopIteration()
else:
raise StopIteration()
class ReadGraph(object):
"""读取文件中的图"""
def __init__(self, graph, filename):
with open(filename, 'r') as f:
line = f.readline()
line = line.strip('\n')
line = line.split()
v = int(line[0])
e = int(line[1])
if v == graph.V():
lines = f.readlines()
for i in lines:
a, b, w = self.stringstream(i)
if a >= 0 and a < v and b >=0 and b < v:
graph.addEdge(a, b, w)
def stringstream(self, text):
result = text.strip('\n')
result = result.split()
a, b, w = result
return int(a), int(b), float(w)
class MinHeap(object):
"""最小堆"""
def __init__(self):
self.data = [] # 创建堆
self.count = len(self.data) # 元素数量
# def __init__(self, arr):
# self.data = copy.copy(arr)
# self.count = len(self.data)
# i = self.count / 2
# while i >= 1:
# self.shiftDown(i)
# i -= 1
def size(self):
return self.count
def isEmpty(self):
return self.count == 0
def insert(self, item):
# 插入元素入堆
self.data.append(item)
self.count += 1
self.shiftup(self.count)
def shiftup(self, count):
# 将插入的元素放到合适位置,保持最小堆
while count > 1 and self.data[(count/2)-1] > self.data[count-1]:
self.data[(count/2)-1], self.data[count-1] = self.data[count-1], self.data[(count/2)-1]
count /= 2
def extractMin(self):
# 出堆
if self.count > 0:
ret = self.data[0]
self.data[0], self.data[self.count-1] = self.data[self.count-1], self.data[0]
self.data.pop()
self.count -= 1
self.shiftDown(1)
return ret
def shiftDown(self, count):
# 将堆的索引位置元素向下移动到合适位置,保持最小堆
while 2 * count <= self.count :
# 证明有孩子
j = 2 * count
if j + 1 <= self.count:
# 证明有右孩子
if self.data[j] < self.data[j-1]:
j += 1
if self.data[count-1] <= self.data[j-1]:
# 堆的索引位置已经小于两个孩子节点,不需要交换了
break
self.data[count-1], self.data[j-1] = self.data[j-1], self.data[count-1]
count = j
class LazyPrimMST(object):
"""最小生成树T,每次选取横切边中权值最小的边,将另一端顶点加入树中"""
def __init__(self, graph):
self.G = graph # 传入图
self.pq = MinHeap() # 最小堆,用于选择权值最小的边
self.marked = [False for _ in range(self.G.V())] # 用于标记已经被选取为树的节点,初始都为False
self.mst = [] # 记录被选取的边
self.mstWeight = 0 # 记录最小生成树的总权值
# Lizy Prim
self.visit(0)
while not self.pq.isEmpty():
# 取出权值最小的横切边
e = self.pq.extractMin()
if self.marked[e.v()] == self.marked[e.w()]:
# 如果e不是横切边(比如两个顶点都已经加入树中)
continue
# 选取e这条边,组成最小生成树
self.mst.append(e)
# 此时e边两个顶点必有一个未加入树中
if not self.marked[e.v()]:
self.visit(e.v())
else:
self.visit(e.w())
self.mstWeight = sum([i.wt() for i in self.mst])
def visit(self, v):
# 访问
if not self.marked[v]:
# self.marked[v]为False,表示该节点还未被加入树中
self.marked[v] = True
adj = self.G.adjIterator(self.G, v)
for i in adj:
if not self.marked[i.other(v)]:
# 与v相连接的另一端还未被加入树中,则这一条边为横切边,加入到最小堆中
# 此处Edge类重载运算符,可直接放入堆中进行计算
self.pq.insert(i)
def mstEdges(self):
# 查询最小生成树的边
return self.mst
def result(self):
# 返回最小生成树的权值
return self.mstWeight
【算法——Python实现】有权图求最小生成树LazyPrim算法
最新推荐文章于 2024-08-13 20:47:36 发布