邻接表实现Prim算法

Prim算法

Prim算法是通过连通网找最小生成树(mst)的算法
过程:
1.首先选取任意顶点作为树的根
2.遍历其他(不在树中)的顶点,对于每个顶点,找到一条到mst路径最短的边,这样,每个顶点都有一条通往mst的边(不是邻接的就设权值无限大)

这里路径的意思是两顶点的直接距离,不相邻的顶点,路径距离就是∞,否则就是这条边的权值

3.在所有的边中,找到权值最小的一条,将对应顶点加入到mst中
循环2,3(对于n个顶点,循环n-1次)


例如,求下图的最小生成树
在这里插入图片描述
首先,选择v0作为起点,其他顶点到mst的距离分别为[0, 4, ∞, ∞, 3, 1]
在这里插入图片描述

从这6条边中选择最小的一条(非零)(v0, v5),将v5加入到mst中:

在这里插入图片描述


接着,重复上面步骤,各顶点到mst的最小距离为:[0, 4, 7, 8, 3, 0]
在这里插入图片描述

v2从(v2, v0) 和 (v2, v5)中选择了(v2, v5),与树的距离从原来的无穷大更新为7,同样,v3也是这样,从∞更新为8

从这些边中选择最小的非零边(v0, v4),将v4加入到mst中
…一直循环,最终结果如下:
在这里插入图片描述



代码实现

使用邻接表来存储图结构:

typedef struct EdgeNode{
    int adjIndex;   //邻接到下标为adjindex的顶点
    WEIGHT weight;
    struct EdgeNode * next;
}EdgeNode;			//边节点

typedef struct VertexNode{
    DataElement data;
    int id;
    EdgeNode* firstEdge;
}VertexNode;		//顶点节点

typedef struct AdjList{
    VertexNode* VerNode[MAXSIZE];
    int verQuantity;    //顶点数量
    int edgeQuantity;   //边数量
}AdjList;

创建最小生成树需要三个辅助的数组:

  • bool isAdded[MAXSIZE]
  • int lowcost[MAXSIZE]
  • int adjVex[MAXSIZE]

isAdded储存某个定点是否已经加入到mst中

这个数组在邻接矩阵表示法中是没有的,因为邻接矩阵中可以用0和INFINITY表示顶点已经加入到mst中以及边权值为无穷大,而在邻接表表示法中只能通过指针指向NULL表示边的权值无穷大,因此需要一个辅助数组isAdded

lowcost储存某个结点到mst的最短路径

例如mst中有三个顶点A,B,C,此时一个非mst的顶点D到mst就有三条路径:(A,D),(B,D),(C,D),lowcost[D]储存了最短的一条(B,D)

adjVex储存mst中各个顶点中到其他顶点路径最短的那个顶点

比如上例中mst中各个顶点到顶点D最短的为B顶点,则adjVex[D]储存了顶点B

这三个数组是从算法思路上需要的,具体的实现需要根据实际代码修改

/** 普里姆算法生成最小生成树 */
void PrimCreat(AdjList* adj)
{
    int min;        //最小权值
    int minIndex;   //最小权值的边的下标
    int isAdded[MAXSIZE] = {0};
    EdgeNode*  lowcost[MAXSIZE] = {NULL};                    //保存边的信息,如果为NULL,则表示权值无穷大
    VertexNode* adjVex[MAXSIZE] = {NULL};                    //保存另一顶点信息   Vi 与 Vadjvex[i] 的权值为lowcost[i]
    
    EdgeNode* temp = adj->VerNode[0]->firstEdge;    
    isAdded[0] = 1;
    for(int i = 0; i < adj->verQuantity; i++)               //两个数组的初始化
    {
        adjVex[adj->VerNode[i]->id] = adj->VerNode[0];       //都指向第一个顶点
        if(temp)
        {
            lowcost[temp->adjIndex] = temp;
            temp = temp->next;
        }
    }

    for(int i = 0; i < adj->verQuantity - 1; i++)
    {
        min = INFINITY;
        //首先找到lowcost中权值最小的边以及对应的非mst顶点下标
        for(int k = 0; k < adj->verQuantity; k++)
        {
            if(lowcost[k] != NULL && lowcost[k]->weight < min)
            {
                min = lowcost[k]->weight;
                minIndex = k;
            }
        }
        //打印
        printf("(%d, %d)%d - ", adjVex[minIndex]->id, minIndex, min);

        isAdded[minIndex] = 1;
        //minIndex这个顶点已经加入到mst中了,它与mst的距离为0了(这里没有另一种状态表示距离为0,只能指向NULL)
        //如果没有isAdded这个数组,则不能区分lowcost中NULL是表示距离为0还是距离为无穷大
        lowcost[minIndex] = NULL;

        //更新lowcost数组
        //mst中新加了一个顶点,所有的更新都与这个顶点相关,因此用与依附于这个顶点的所有边进行更新
        temp = adj->VerNode[minIndex]->firstEdge;
        while(temp)
        {
            //更新就是找到一条更短的路径,因此对方顶点不在mst中并且权值更小就可以更新
            if(isAdded[temp->adjIndex] != 1 && (lowcost[temp->adjIndex] == NULL || temp->weight < lowcost[temp->adjIndex]->weight))
            {
                lowcost[temp->adjIndex] = temp;
                adjVex[temp->adjIndex] = adj->VerNode[minIndex];    //同时更新adjVex数组
            }
            temp = temp->next;
        }
    }
}

具体的思路都写在注释里面了

  • 10
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是邻接表Prim算法实现: ``` #include <iostream> #include <vector> #include <queue> #include <cstring> using namespace std; const int MAXN = 1005; const int INF = 0x3f3f3f3f; struct Edge { int to, weight; Edge(int t, int w) : to(t), weight(w) {} }; vector<Edge> G[MAXN]; // 邻接表 int dist[MAXN]; // 到MST的最短距离 bool vis[MAXN]; // 是否已加入MST int n, m; struct Node { // 用于优先队列中的比较 int u, d; Node(int u, int d) : u(u), d(d) {} bool operator<(const Node &rhs) const { return d > rhs.d; } }; void Prim(int start) { memset(dist, INF, sizeof(dist)); memset(vis, false, sizeof(vis)); dist[start] = 0; priority_queue<Node> pq; pq.push(Node(start, 0)); while (!pq.empty()) { int u = pq.top().u; pq.pop(); if (vis[u]) continue; vis[u] = true; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i].to; int w = G[u][i].weight; if (!vis[v] && w < dist[v]) { dist[v] = w; pq.push(Node(v, dist[v])); } } } } int main() { cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v, w; cin >> u >> v >> w; G[u].push_back(Edge(v, w)); G[v].push_back(Edge(u, w)); } Prim(1); int ans = 0; for (int i = 1; i <= n; i++) { if (dist[i] == INF) { // 无法到达 cout << "No MST!" << endl; return 0; } ans += dist[i]; } cout << "MST weight: " << ans << endl; return 0; } ``` 说明: 1. 首先我们定义了一个邻接表`G`,其中`G[i]`表示编号为`i`的点所连接的所有边。 2. 然后,我们定义了一个距离数组`dist`,表示每个点到MST的最短距离,初始时全部赋值为INF。 3. `vis`表示每个点是否已经被加入MST,初始时全部赋值为false。 4. 首先将起点加入MST,然后将起点的所有邻接点加入优先队列中,其中优先队列按照到MST的距离从小到大排序。 5. 从优先队列中取出距离MST最近的点u,并将其加入MST中。 6. 然后,遍历u的所有邻接点v,如果v未被加入MST且u到v的距离比目前v到MST的距离更短,则更新dist[v]的值,并将v加入优先队列。 7. 重复步骤5和6,直到所有点都被加入MST为止。 8. 最后遍历dist数组,如果有点无法到达MST,则说明无法构建MST,输出"No MST!",否则输出MST的总权值。 时间复杂度:$O(m\log n)$,其中$m$为边数,$n$为点数。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zh³~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值