最小生成树问题-prim算法

prim适合稠密图,权值不可为负,不能有回路(保证不重复收录节点即可)

具体图标演示看这个:https://www.cnblogs.com/ssyfj/p/9488723.html

知识点:
1)以其中某一顶点为起点,逐步寻找各个顶点上最小权值的边来构建最小生成树。其中运用到了回溯,贪心的思想。

(一)使用邻接矩阵结构

邻接矩阵定义

1 typedef struct{
2     VertexType vertex[MaxNum];    //顶点一维数组
3     EdgeType edge[MaxNum][MaxNum];//路径权值二维数组
4     int vexNum,arcNum;            //顶点数,弧数
5 }MGraph;

记忆算法过程

复制代码
void prim(MGraph G)

{

定义顶点下标数组a;

定义相关顶点的边权值数组b;

初始化第一个顶点权值为0 ;(权值的元素为0,代表该下标对应的顶点已加入生成树)

初始化顶点数组a首元素为0(顶点v0);

循环操作
   将a数组首元素0(顶点v0)的所有边权值放入数组b并初始化顶点数组元 
   素全部为0

开始进行主旋律

1层循环:

(1)循环操作找出目前权值数组b中不为0的最小的那条边 并记录下标为k(此下标代表顶点Vk);

数组b中k位置设为0,即把该条边加入生成树;

(2)将第k个顶点的权值与数组b中权值顺序依次比较,如果b中值为0跳过,如果小于b中该顶点(j)权值,则替换权值b【j】=G.arc【k】【j】,并将顶点数组a【j】=k

2层循环

(1)找出目前权值数组b中不为0最小的那条边 并记录下标k1(此下标代表顶点Vk1);

数组b中k1位置设为0,即将该条边加入生成树;

(2)将第k1个顶点的权值与数组b中权值顺序依次比较,如果b中值为0跳过,如果小于b中该顶点(j1)权值,则替换权值b【j1】=G.arc【k1】【j1】,并将顶点数组a【j1】=k1

3层循环

(1)找出目前权值数组b中不为0最小的那条边 并记录下标k2(此下标为顶点)

数组b中k2位置设为0,即将该条边加入生成树,根据顶点数组a【k2】输出改点

(2)将第k2个顶点的权值与数组b中权值顺序依次比较,如果b中值为0跳过,如果小于b中该顶点(j2)权值,则替换权值b【j1】=G.arc【k2】【j2】,并将顶点数组a【j2】=k2

。。。。。。。

}
复制代码

算法实现代码(与迪杰斯特拉算法有异曲同工之妙)

复制代码
void prim(MGraph G)
{
    int min, i, j, k;
    int adjvex[MAXV];     //保存相关顶点下标
    int lowcost[MAXV];    //保存相关顶点间边的权值
    lowcost[0] = 0;    //初始化第一个权值为0,即将v0加入生成树
    //lowcost的值为0表示此下标的顶点已经加入生成树
    adjvex[0] = 0;    //初始化第一个顶点下标为0
    for (i = 1; i < G.Vnum;i++)
    {
        lowcost[i] = G.arc[0][i]; //将v0顶点与之有关的边的权值都存放入权值数组
        adjvex[i] = 0;    //初始化都为v0的下标
    }

    for (i = 1; i < G.Vnum;i++)
    {
        min = 65535;
        j = 1;
        min_index = 0;
        while (j<G.Vnum)//找最小值 并记录节点下标为k
        {
            if (lowcost[j]!=0&&lowcost[j]<min)
            {
                //如果权值不为0且权值小于min
                min = lowcost[j];    //则让当前权值成为最小值
                min_index = j;    //将当前最小值的下标存放k
            }
            j++;
        }
        printf("(%d,%d)", adjvex[min_index], min_index);    //打印当前顶点边中权值最小边
        lowcost[min_index] = 0;//将当前顶点的权值置为0,表示此顶点已经完成任务
        //更新权值数组
        for (j = 1; j < G.Vnum;j++)    //循环所有顶点,因为我们已经确认第一个放入的0,所有我们循环可以省去0
        {
            if (lowcost[j] != 0 && G.arc[min_index][j]<lowcost[j])
            { /* 如果下标为min_index顶点各边权值小于此前这些顶点未被加入生成树权值 */
                lowcost[j] = G.arc[min_index][j];    //将较小权值存入lowcost
                adjvex[j] = min_index;        //将下标为min_index的顶点存入adjvex
            }
        }
    }
}
复制代码

 (二)使用邻接表结构

准备:

复制代码
#define MAX_NUM 100
typedef char node_type;

typedef struct arc_node
{
    int pos;
    int distance;
    struct arc_node * next;
} Arc_node;//保存Node节点的相邻节点信息

typedef struct node
{
    node_type info;
    Arc_node * next;
} Node;//保存节点信息

typedef struct graph
{
    Node vertexs[MAX_NUM];
    int vertex, arc;
} Graph;//邻接表
复制代码

 

实现:

复制代码
static void init_prim(Graph * graph, Graph * prim_tree);
void g_prim(Graph * graph, Graph * prim_tree)
{
    bool visited[graph->vertexs];
    int i, j, k;
    int power, pos;
    Arc_node * tmp;

    for ( i = 0; i < graph->vertexs; i++ )
        visited[i] = false;
    init_prim(graph, prim_tree);

    visited[0] = true;
    for ( i = 0; i < graph->vertexs; i++ )
    {
        power = INT_MAX;//limits.h
        for ( j = 0; j < graph->vertexs; j++ )
        {
            if ( visited[j] )
            {
                tmp = graph->adjlist[j].next;
                while ( tmp != NULL )
                {
                    if ( power > tmp->distance && !visited[tmp->pos] )
                    {
                        power = tmp->distance;
                        pos = tmp->pos;
                        k = j;
                    }
                    tmp = tmp->next;
                }
            }
        }
        if ( !visited[pos] )
        {
            if ( prim_tree->adjlist[k].next == NULL )
            {
                prim_tree->adjlist[k].next = make_node(pos, power);
            }
            else
            {
                tmp = prim_tree->adjlist[k].next;
                while ( tmp->next != NULL )
                    tmp = tmp->next;
                tmp->next = make_node(pos, power);
            }
            visited[pos] = true;
        }
    }
}

static void init_prim(Graph * graph, Graph * prim_tree)
{
    int i;

    for ( i = 0; i < graph->vertexs; i++ )
    {
        prim_tree->adjlist[i].info = graph->adjlist[i].info;
        prim_tree->adjlist[i].next = NULL;
    }
    prim_tree->vertexs = graph->vertexs;
}
复制代码

 

posted on   Y-flower  阅读(238)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示