数据结构与算法——最小生成树问题(什么是最小生成树、Prim算法、Kruskal算法)

news2024/11/16 9:29:33

目录

什么是最小生成树

贪心算法

Prim算法

思路

代码(C语言)

Kruskal算法

思路

代码


什么是最小生成树

1.是一颗

  • 无回路
  • \left | V \right |个顶点一定有\left | V \right |-1条边

2.是生成

  • 包含全部顶点
  • \left | V \right |-1条边都在图里

3.边的权重和最小

向生成树中任加一条边都一定构成回路

最小生成树存在\leftrightarrow图连通

贪心算法

在最小生成树的问题中,运用贪心算法。

什么是“贪”:每一步都要最好的。

什么是“好”:权重最小的边。

需要约束

  • 只能用图里有的边
  • 只能正好用掉 \left | V \right |-1条边
  • 不能有回路

Prim算法

思路

(适用于稠密图)

prim算法先选定一个顶点收录,然后以该顶点开始,往外找到权重最小的顶点,且该权重最小的顶点要与当前生成的最小树有关联,过程即:

代码(C语言)

/* 邻接矩阵存储 - Prim最小生成树算法 */

Vertex FindMinDist( MGraph Graph, WeightType dist[] )
{ /* 返回未被收录顶点中dist最小者 */
    Vertex MinV, V;
    WeightType MinDist = INFINITY;

    for (V=0; V<Graph->Nv; V++) {
        if ( dist[V]!=0 && dist[V]<MinDist) {
            /* 若V未被收录,且dist[V]更小 */
            MinDist = dist[V]; /* 更新最小距离 */
            MinV = V; /* 更新对应顶点 */
        }
    }
    if (MinDist < INFINITY) /* 若找到最小dist */
        return MinV; /* 返回对应的顶点下标 */
    else return ERROR;  /* 若这样的顶点不存在,返回-1作为标记 */
}

int Prim( MGraph Graph, LGraph MST )
{ /* 将最小生成树保存为邻接表存储的图MST,返回最小权重和 */
    WeightType dist[MaxVertexNum], TotalWeight;
    Vertex parent[MaxVertexNum], V, W;
    int VCount;
    Edge E;
    
    /* 初始化。默认初始点下标是0 */
       for (V=0; V<Graph->Nv; V++) {
        /* 这里假设若V到W没有直接的边,则Graph->G[V][W]定义为INFINITY */
           dist[V] = Graph->G[0][V];
           parent[V] = 0; /* 暂且定义所有顶点的父结点都是初始点0 */ 
    }
    TotalWeight = 0; /* 初始化权重和     */
    VCount = 0;      /* 初始化收录的顶点数 */
    /* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
    MST = CreateGraph(Graph->Nv);
    E = (Edge)malloc( sizeof(struct ENode) ); /* 建立空的边结点 */
           
    /* 将初始点0收录进MST */
    dist[0] = 0;
    VCount ++;
    parent[0] = -1; /* 当前树根是0 */

    while (1) {
        V = FindMinDist( Graph, dist );
        /* V = 未被收录顶点中dist最小者 */
        if ( V==ERROR ) /* 若这样的V不存在 */
            break;   /* 算法结束 */
            
        /* 将V及相应的边<parent[V], V>收录进MST */
        E->V1 = parent[V];
        E->V2 = V;
        E->Weight = dist[V];
        InsertEdge( MST, E );
        TotalWeight += dist[V];
        dist[V] = 0;
        VCount++;
        
        for( W=0; W<Graph->Nv; W++ ) /* 对图中的每个顶点W */
            if ( dist[W]!=0 && Graph->G[V][W]<INFINITY ) {
            /* 若W是V的邻接点并且未被收录 */
                if ( Graph->G[V][W] < dist[W] ) {
                /* 若收录V使得dist[W]变小 */
                    dist[W] = Graph->G[V][W]; /* 更新dist[W] */
                    parent[W] = V; /* 更新树 */
                }
            }
    } /* while结束*/
    if ( VCount < Graph->Nv ) /* MST中收的顶点不到|V|个 */
       TotalWeight = ERROR;
    return TotalWeight;   /* 算法执行完毕,返回最小权重和或错误标记 */
}

Kruskal算法

思路

(适用于稀疏图)

其基本思想是把森林合并成一棵树。每次把权重最小的边收录进去,操作中注意不能构成回路,重复操作直到把所有边都收录进去。

 

代码

/* 邻接表存储 - Kruskal最小生成树算法 */

/*-------------------- 顶点并查集定义 --------------------*/
typedef Vertex ElementType; /* 默认元素可以用非负整数表示 */
typedef Vertex SetName;     /* 默认用根结点的下标作为集合名称 */
typedef ElementType SetType[MaxVertexNum]; /* 假设集合元素下标从0开始 */

void InitializeVSet( SetType S, int N )
{ /* 初始化并查集 */
    ElementType X;

    for ( X=0; X<N; X++ ) S[X] = -1;
}

void Union( SetType S, SetName Root1, SetName Root2 )
{ /* 这里默认Root1和Root2是不同集合的根结点 */
    /* 保证小集合并入大集合 */
    if ( S[Root2] < S[Root1] ) { /* 如果集合2比较大 */
        S[Root2] += S[Root1];     /* 集合1并入集合2  */
        S[Root1] = Root2;
    }
    else {                         /* 如果集合1比较大 */
        S[Root1] += S[Root2];     /* 集合2并入集合1  */
        S[Root2] = Root1;
    }
}

SetName Find( SetType S, ElementType X )
{ /* 默认集合元素全部初始化为-1 */
    if ( S[X] < 0 ) /* 找到集合的根 */
        return X;
    else
        return S[X] = Find( S, S[X] ); /* 路径压缩 */
}

bool CheckCycle( SetType VSet, Vertex V1, Vertex V2 )
{ /* 检查连接V1和V2的边是否在现有的最小生成树子集中构成回路 */
    Vertex Root1, Root2;

    Root1 = Find( VSet, V1 ); /* 得到V1所属的连通集名称 */
    Root2 = Find( VSet, V2 ); /* 得到V2所属的连通集名称 */

    if( Root1==Root2 ) /* 若V1和V2已经连通,则该边不能要 */
        return false;
    else { /* 否则该边可以被收集,同时将V1和V2并入同一连通集 */
        Union( VSet, Root1, Root2 );
        return true;
    }
}
/*-------------------- 并查集定义结束 --------------------*/

/*-------------------- 边的最小堆定义 --------------------*/
void PercDown( Edge ESet, int p, int N )
{ /* 改编代码4.24的PercDown( MaxHeap H, int p )    */
  /* 将N个元素的边数组中以ESet[p]为根的子堆调整为关于Weight的最小堆 */
    int Parent, Child;
    struct ENode X;

    X = ESet[p]; /* 取出根结点存放的值 */
    for( Parent=p; (Parent*2+1)<N; Parent=Child ) {
        Child = Parent * 2 + 1;
        if( (Child!=N-1) && (ESet[Child].Weight>ESet[Child+1].Weight) )
            Child++;  /* Child指向左右子结点的较小者 */
        if( X.Weight <= ESet[Child].Weight ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            ESet[Parent] = ESet[Child];
    }
    ESet[Parent] = X;
}

void InitializeESet( LGraph Graph, Edge ESet )
{ /* 将图的边存入数组ESet,并且初始化为最小堆 */
    Vertex V;
    PtrToAdjVNode W;
    int ECount;

    /* 将图的边存入数组ESet */
    ECount = 0;
    for ( V=0; V<Graph->Nv; V++ )
        for ( W=Graph->G[V].FirstEdge; W; W=W->Next )
            if ( V < W->AdjV ) { /* 避免重复录入无向图的边,只收V1<V2的边 */
                ESet[ECount].V1 = V;
                ESet[ECount].V2 = W->AdjV;
                ESet[ECount++].Weight = W->Weight;
            }
    /* 初始化为最小堆 */
    for ( ECount=Graph->Ne/2; ECount>=0; ECount-- )
        PercDown( ESet, ECount, Graph->Ne );
}

int GetEdge( Edge ESet, int CurrentSize )
{ /* 给定当前堆的大小CurrentSize,将当前最小边位置弹出并调整堆 */

    /* 将最小边与当前堆的最后一个位置的边交换 */
    Swap( &ESet[0], &ESet[CurrentSize-1]);
    /* 将剩下的边继续调整成最小堆 */
    PercDown( ESet, 0, CurrentSize-1 );

    return CurrentSize-1; /* 返回最小边所在位置 */
}
/*-------------------- 最小堆定义结束 --------------------*/


int Kruskal( LGraph Graph, LGraph MST )
{ /* 将最小生成树保存为邻接表存储的图MST,返回最小权重和 */
    WeightType TotalWeight;
    int ECount, NextEdge;
    SetType VSet; /* 顶点数组 */
    Edge ESet;    /* 边数组 */

    InitializeVSet( VSet, Graph->Nv ); /* 初始化顶点并查集 */
    ESet = (Edge)malloc( sizeof(struct ENode)*Graph->Ne );
    InitializeESet( Graph, ESet ); /* 初始化边的最小堆 */
    /* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
    MST = CreateGraph(Graph->Nv);
    TotalWeight = 0; /* 初始化权重和     */
    ECount = 0;      /* 初始化收录的边数 */

    NextEdge = Graph->Ne; /* 原始边集的规模 */
    while ( ECount < Graph->Nv-1 ) {  /* 当收集的边不足以构成树时 */
        NextEdge = GetEdge( ESet, NextEdge ); /* 从边集中得到最小边的位置 */
        if (NextEdge < 0) /* 边集已空 */
            break;
        /* 如果该边的加入不构成回路,即两端结点不属于同一连通集 */
        if ( CheckCycle( VSet, ESet[NextEdge].V1, ESet[NextEdge].V2 )==true ) {
            /* 将该边插入MST */
            InsertEdge( MST, ESet+NextEdge );
            TotalWeight += ESet[NextEdge].Weight; /* 累计权重 */
            ECount++; /* 生成树中边数加1 */
        }
    }
    if ( ECount < Graph->Nv-1 )
        TotalWeight = -1; /* 设置错误标记,表示生成树不存在 */

    return TotalWeight;
}

end


学习自:MOOC数据结构——陈越、何钦铭

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/739254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端vue入门(纯代码)25_路由vueRouter

如果耐不住寂寞&#xff0c;你就看不到繁华。 【23.Vue Router--路由】 [可以去官网看看Vue Router文档](入门 | Vue Router (vuejs.org)) 用 Vue Vue Router 创建单页应用非常简单&#xff1a;通过 Vue.js&#xff0c;我们已经用组件组成了我们的应用。当加入 Vue Router …

Linux基础服务9——lamt架构

文章目录 一、基本了解二、安装tomcat三、 常用文件3.1 bin目录文件3.2 conf目录文件3.2.1 server.xml文件3.2.2 tomcat-users.xml 文件 四、分离部署lamt架构4.1 安装httpd4.2 安装mysql4.3 部署tomcat4.4 配置apache 一、基本了解 前提背景&#xff1a; Tomcat是Apache 软件基…

Git开发项目完整流程使用(图文超详解)

前世今生 自2002年开始&#xff0c;林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;决定使用BitKeeper作为Linux内核主要的版本控制系统用以维护代码。因为BitKeeper为专有软件&#xff0c;这个决定在社群中长期遭受质疑。在Linux社群中&#xff0c;特别是理查德斯托曼与…

springboot电子招投标系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

MySQL基础篇第5章(排序与分页)

文章目录 1、排序1.1 排序规则1.2 单列排序1.3 多列排序 2、分页2.1 背景2.2 实现规则2.3 拓展 1、排序 1.1 排序规则 使用 ORDER BY 子句排序 ASC&#xff08;ascend&#xff09;: 升序DESC&#xff08;descend&#xff09;:降序 ORDER BY 子句在SELECT语句的结尾。 1.2 …

lumpy_sv的安装

目前利用lumpy-SV进行鉴定SV是比较常用的软件&#xff0c;但是其依赖python2.7版本的环境 ## 创建python 2.7版本conda环境 conda create -n python27 python2.7 ## 进入py27环境 conda activate python27 ## 下载lumpy软件并安装 git clone --recursive https://github.com/a…

深度学习——搭建神经网络的两种方式

方法一&#xff1a;定义神经网络类&#xff0c;然后实例化 代码&#xff1a; import torch import torch.nn.functional as F"""方法一&#xff1a;定义神经网络类&#xff0c;然后再实例化 """ # 神经网络类 class Net(torch.nn.Module):def …

CRYPTO-36D-justShow

0x00 前言 CTF 加解密合集&#xff1a;CTF 加解密合集 0x01 题目 hlcgoyfsjqknyifnpd:bcdefghijklmnopqrstuvwxyza0x02 Write Up 这道题也是完全击中了我的软肋&#xff0c;用到了新的加密方式&#xff0c;并且感觉到自己对这种类型的题目并不是很敏感。 首先看到hlcgoyfs…

基于matlab自动检测图像中的圆形目标并可视化检测到的圆(附源码)

一、前言 此示例说明如何自动检测图像中的圆或圆形目标并可视化检测到的圆。 二、实现步骤 步骤 1&#xff1a;加载图像 读取并显示包含各种颜色的圆形塑料片的图像。除了有大量要检测的圆之外&#xff0c;从圆检测的角度来看&#xff0c;此图像还有一些有趣的特点&#xf…

Sping Security前后端分离两种方案

前言 本篇文章是基于Spring Security实现前后端分离登录认证及权限控制的实战&#xff0c;主要包括以下四方面内容: Spring Seciruty简单介绍&#xff1b; 通过Spring Seciruty实现的基于表单和Token认证的两种认证方式; 自定义实现RBAC的权限控制&#xff1b; 跨域问题处理…

Django_模板继承

模板继承先创建一个父模版&#xff0c;它包含大部页面共有元素&#xff0c;并且需要定义能够被子模板覆盖的blocks标签。 extends 模板继承 通过下面的例子&#xff0c;理解模板继承的概念。 创建base.html文件&#xff0c;写入下面代码&#xff1a; <!DOCTYPE html>…

Stable Diffusion - 墨幽人造人 模型与 Tag 配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131565068 墨幽人造人模型的版本介绍&#xff1a; v1010修剪&#xff1a;更小体积的4G修剪版&#xff0c;但是整体色彩及细节表现力、好手好脸概…

结构体和数据结构--动态数据结构体-单向链表

目录 一、问题的提出 二、链表的定义 三、单向链表的建立 四、单向链表的删除操作 五、单向链表的插入操作 一、问题的提出 数组实质是一种顺序存储&#xff0c;随机访问的线性表&#xff0c;它的优点是使用直观&#xff0c;便于快速、随机地存取线性表中地任意元素。但缺…

线性表综合应用题1

线性表综合应用题1 从顺序表中删除具有最小值的元素&#xff08;假设唯一&#xff09;并由函数返回被删除元素的值。空出的位置由最后一个元素填补&#xff0c;若顺序表为空&#xff0c;则显示出错信息并退出运行。 算法思想&#xff1a;搜索整个顺序表&#xff0c;查找最小值元…

多元回归预测 | Matlab基于鲸鱼算法(WOA)优化高斯过程回归(WOA-GPR)的数据回归预测,matlab代码,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于鲸鱼算法(WOA)优化高斯过程回归(WOA-GPR)的数据回归预测,matlab代码,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源…

ct.js笔记-整合SweetAlert2,弹出输入框(添加Cat模组)

如下&#xff1a; 效果如下&#xff1a; 点击Name后&#xff1a; 实现如下&#xff1a; 在ct.libs中添加个新文件夹sweetalert2文件夹。 内部要包含这几个关键文件&#xff1a; ①index.js&#xff1a;调用模组的入库文件&#xff1b; ②includes文件夹&#xff1a;把需要引…

keepalived 实现 IP 地址漂移

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

基于redis实现延时队列(二)

背景 上篇文章中使用了redis的zset定时器实现延时任务&#xff0c;虽然定时器设置为30秒执行一次&#xff0c;但是还是有时间上的差异化&#xff1b;现更换一种方式实现&#xff0c;可以避免时间上的差异。 redis的key过期回调事件&#xff0c;也能达到延迟队列效果 配置修改…

抖音短视频seo矩阵源码开源SaaS部署(四)

目录 一、 抖音短视频SEO矩阵包括以下内容&#xff1a; ​编辑 二、 源码开源部署流程 三、 SaaS的理解 四、 抖音短视频seo&#xff0c;SaaS矩阵部署用到的技术 抖音短视频SEO主要是通过以下几个方面实现的&#xff1a; SaaS矩阵部署主要用到的技术包括以下几个方面&…

深蓝学院C++基础与深度解析笔记 第 11 章 类

深蓝学院C基础与深度解析笔记 第 11 章 类 1. 结构体与对象聚合 **● 结构体&#xff1a;**对基本数据结构进行扩展&#xff0c;将多个对象放置在一起视为一个整体 – 结构体的声明与定义&#xff08;注意定义后面要跟分号来表示结束&#xff09; – 仅有声明的结构体是不完全…