最小生成树算法-prim与kruskal

生成树:
即n个顶点构成只有n-1条边的树
算法:
加边(Kruskal):
依照最短路径从最近到最远,构成n/2棵子树,子树间最短路径连通,依次循环
加点(prim):
使用两个集合,一个集合A为最小生成树中的点,另一个B为未加入的点,每次寻找两集合点中最小值,并将对应B中点删除,加入A

py代码:
力扣1584
最小生成树算法-prim与kruskal_第1张图片
最小生成树算法-prim与kruskal_第2张图片
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

然后进行循环比较则得出,但是存在复杂度过高的情况
最小生成树算法-prim与kruskal_第3张图片
优化:引入堆优化
使用堆,堆认为是一种完全二叉树,子节点值总是小于或大于等于其父节点
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


优化结果:
最小生成树算法-prim与kruskal_第4张图片

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与kruskal_第5张图片
相对来说prim的堆优化在时间,内存上都有优势

你可能感兴趣的:(算法,算法,python,数据结构)