生成树:
即n个顶点构成只有n-1条边的树
算法:
加边(Kruskal):
依照最短路径从最近到最远,构成n/2棵子树,子树间最短路径连通,依次循环
加点(prim):
使用两个集合,一个集合A为最小生成树中的点,另一个B为未加入的点,每次寻找两集合点中最小值,并将对应B中点删除,加入A
py代码:
力扣1584
prim算法
def getminus(a,b):
mini = abs(a[0][0]-b[0][0])+abs(a[0][1]-b[0][1])
k=0
for i in a:
for j in b:
if abs(i[0]-j[0])+abs(i[1]-j[1]) <mini:
mini=abs(i[0]-j[0])+abs(i[1]-j[1])
k=j
#print(mini,a[i],b[j],k)
b.remove(k)
return mini,k,b
然后进行循环比较则得出,但是存在复杂度过高的情况
优化:引入堆优化
使用堆,堆认为是一种完全二叉树,子节点值总是小于或大于等于其父节点
python可以import heapq,列表中第i个元素总是大于i//2位置的元素,heapq[0]最小
heappush(heap, x)#推进x进堆
heappop(heap)#从堆中弹出最小的元素
heapify(heap)#让列表具备堆特征
heapreplace(heap, x)#弹出最小的元素,并将x压入堆中,替换
nlargest(n, i)#返回i中n个最大的元素[]
nsmallest(n, i)#返回n个i中最小元素
class Solution:
def minCostConnectPoints(self, points: List[List[int]]) -> int:
def getmin(a,b):#a为列表,b为点,不需要直到a中哪个点连接,只需要知道b点
#print(a,b)
mini = abs(a[0][0]-b[0])+abs(a[0][1]-b[1])
for i in range(len(a)):
if abs(a[i][0]-b[0])+abs(a[i][1]-b[1]) <mini:
mini=abs(a[i][0]-b[0])+abs(a[i][1]-b[1])
return mini
#使用堆可以每次弹出最小距离(树到点),此时只需再计算加进来的点与各剩余顶点间的距离,若小于对应的最小距离,则替换堆中值
import heapq
heap=[]
record=[]
minis=0
#选取points最后一个点为起点
a=[]
a.append(points[-1])
points=points[:-1]
#print(a)
for i in range(len(points)):
heapq.heappush(heap,getmin(a,points[i]))
record.append(getmin(a,points[i]))
#record中,第i个位置对应points[i]到树最小值
#print(heap)
#删除列表:记录最短路径对应的i
while heap:
#print(heap,record)
minii=heap[0]
#print(minii)
heap.pop()
minis+=minii
#print(minis)
i=record.index(minii)
#print(i)
#直接删除表points与record对应值,剩下值仍对应
a.append(points[i])
del(points[i])
del(record[i])
#print(record)
#对于新加入的点,计算其与points剩下点的路径,小于对应最小值则替换heap与record中对应值
for j in range(len(points)):
if abs(points[j][0]-a[-1][0])+abs(points[j][1]-a[-1][1]) <record[j]:
record[j]=abs(points[j][0]-a[-1][0])+abs(points[j][1]-a[-1][1])
#print(2)
#重新生成堆
#print(record)
heap=copy.deepcopy(record)
#print(3)
heapq.heapify(heap)
#print(4,heap)
#print(1,minis)
return minis
优化结果:
kruskal:加边,类并查集
class Solution:
def minCostConnectPoints(self, points: List[List[int]]) -> int:
def find(a,parent):
#判断是否有根节点,a代表第几个元素
if parent[a] == a:
return a
else:
parent[a]=find(parent[a],parent)
return parent[a]
#定义可知,即把节点看成树,每次连接最近两棵
a=[]
parent=[i for i in range(len(points))]
for i in range(len(points)):
for j in range(i+1,len(points)):
route=abs(points[i][0]-points[j][0])+abs(points[i][1]-points[j][1])
a.append([i,j,route])
a.sort(key=lambda x: x[2])
#逐个对a取值,如果同树则去除,继续往下,否则加,连接树
total=0
#al里面
for i in a:
if find(i[0],parent) != find(i[1],parent):
total+=i[2]
parent[find(i[0],parent)]=find(i[1],parent)
return total
需要注意的是,如果选择对a数组进行删除首项操作,时间会高很多
相对来说prim的堆优化在时间,内存上都有优势