图论13-最小生成树-Kruskal算法+Prim算法

文章目录

  • [1 最小生成树](#1 最小生成树)
  • [2 最小生成树Kruskal算法的实现](#2 最小生成树Kruskal算法的实现)
    • [2.1 算法思想](#2.1 算法思想)
    • [2.2 算法实现](#2.2 算法实现)
      • [2.2.1 如果图不联通,直接返回空,该图没有mst](#2.2.1 如果图不联通,直接返回空,该图没有mst)
      • [2.2.2 获得图中的所有边,并且进行排序](#2.2.2 获得图中的所有边,并且进行排序)
        • [2.2.2.1 Edge类要实现Comparable接口,并重写compareTo方法](#2.2.2.1 Edge类要实现Comparable接口,并重写compareTo方法)
      • [2.2.3 取边进行判断是否形成环](#2.2.3 取边进行判断是否形成环)
  • [3 最小生成树Prim算法的实现](#3 最小生成树Prim算法的实现)
    • [3.1 算法思想](#3.1 算法思想)
    • [3.2 算法实现](#3.2 算法实现)
      • [3.2.1 如果图不联通,直接返回空,该图没有mst](#3.2.1 如果图不联通,直接返回空,该图没有mst)
      • [3.2.2 使用visited数组区分A组B组](#3.2.2 使用visited数组区分A组B组)
      • [3.2.3 添加边生成mst](#3.2.3 添加边生成mst)
      • [3.2.4 切分优化 - (一定要掌握)](#3.2.4 切分优化 - (一定要掌握))

1 最小生成树

2 最小生成树Kruskal算法的实现

2.1 算法思想

  1. 基本思想:按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路
  2. 具体做法:首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止。

2.2 算法实现

2.2.1 如果图不联通,直接返回空,该图没有mst

c 复制代码
 CC cc = new CC(G);
 
 if(cc.count() > 1) return;

2.2.2 获得图中的所有边,并且进行排序

cpp 复制代码
ArrayList<WeightedEdge> edges = new ArrayList<>();

for(int v = 0; v < G.V(); v ++)
    for(int w: G.adj(v))
        if(v < w) // 剪枝:0-2,2-0,只判断0-2,避免重复
            edges.add(new WeightedEdge(v, w, G.getWeight(v, w)));

Collections.sort(edges);
2.2.2.1 Edge类要实现Comparable接口,并重写compareTo方法
c 复制代码
public int compareTo(WeightedEdge another){
    return weight - another.weight;
}

2.2.3 取边进行判断是否形成环

2.2.3.1判断是否形成环

通过并查集标记联通分量。

如果添加进来的边的两个顶点属于不同的集合,那么说明不会形成环。

如果添加进来的边的两个顶点属于相同的集合,那么说明一定形成环。

c 复制代码
UF uf = new UF(G.V());
for(WeightedEdge edge: edges){
    int v = edge.getV();
    int w = edge.getW();

    // 判断选择的边的两个顶点是否属相连
    if(!uf.isConnected(v, w)){ 
        mst.add(edge);
        uf.unionElements(v, w); // 合并两个顶点和边
    }
}

3 最小生成树Prim算法的实现

3.1 算法思想

Prim的核心思想就是使用贪心算法,每次从连通图中找到一条符合条件的权值最小的边,重复这样的操作N-1次,选出的N-1条权值最小的边组成的树就是最下生成树。

将顶点分为两类,一类是在查找的过程中已经包含在树中的(假设为 B 类),剩下的是另一类(假设为 A 类)。

3.2 算法实现

3.2.1 如果图不联通,直接返回空,该图没有mst

c 复制代码
 CC cc = new CC(G);
 
 if(cc.count() > 1) return;

3.2.2 使用visited数组区分A组B组

初始化的时候visited数组起始的元素为true,其余全部设置为galse,表示两个不同的组

c 复制代码
boolean visited[] = new boolean[G.V()];
visited[0] = true;

3.2.3 添加边生成mst

声明一个变量minEdge用于标记权重最小的边。

c 复制代码
for(int i = 1; i < G.V(); i ++){
    WeightedEdge minEdge = new WeightedEdge(-1, -1, Integer.MAX_VALUE);
    for(int v = 0; v < G.V(); v ++)
        if(visited[v]) //当前组的节点进行遍历
            for(int w: G.adj(v)) //找到相邻的顶点
            // 如果当前的顶点跟上一个顶点不是一个组
            // 并且权重比minEdge标记的权重更小
                if(!visited[w] && G.getWeight(v, w) < minEdge.getWeight())
                // 更新minEdge的值,并加入到mst中
                    minEdge = new WeightedEdge(v, w, G.getWeight(v, w));
    mst.add(minEdge);
    visited[minEdge.getV()] = true;
    visited[minEdge.getW()] = true;
}

3.2.4 切分优化 - (一定要掌握)

使用优先队列取最短的边。

拓展的过程中,优先队列的边不一定是合法的边。

在构建mst时进行判断边的合法性。

cpp 复制代码
 Queue pq = new PriorityQueue<WeightedEdge>();
 // 初始化
 for(int w: G.adj(0))
     pq.add(new WeightedEdge(0, w, G.getWeight(0, w)));

// 循环取边
 while(!pq.isEmpty()){

     WeightedEdge minEdge = (WeightedEdge) pq.remove();

		// 判断边的合法性
     if(visited[minEdge.getV()] && visited[minEdge.getW()])
         continue; //  继续循环取边

     mst.add(minEdge);

     int newv = visited[minEdge.getV()] ? minEdge.getW() : minEdge.getV(); // 找到新边不属于同一集合的点
     visited[newv] = true; //设置为同一集合
	
	 // 更新横切边的优先队列
     for(int w: G.adj(newv))
         if(!visited[w])
             pq.add(new WeightedEdge(newv, w, G.getWeight(newv, w)));
 }
相关推荐
DK2215112 分钟前
机器学习系列-----主成分分析(PCA)
人工智能·算法·机器学习
oliveira-time18 分钟前
爬虫学习8
开发语言·javascript·爬虫·python·算法
点云侠1 小时前
二维椭圆拟合算法及推导过程
开发语言·c++·算法·计算机视觉·matlab
一直学习永不止步1 小时前
LeetCode题练习与总结:迷你语法分析器--385
java·数据结构·算法·leetcode·字符串··深度优先搜索
Dragonlongbo1 小时前
leetcode01 --- 环形链表判定
算法·leetcode·职场和发展
-cc- Lynn1 小时前
链表类算法【leetcode】
算法·leetcode·链表
顾京2 小时前
基于扩散模型的表单插补
人工智能·深度学习·算法
chan_lay2 小时前
图论导引 - 第二章 - 11/08
图论
小冉在学习2 小时前
day55 图论章节刷题Part07([53.寻宝]prim算法、kruskal算法)
java·算法·图论
学无止境\n2 小时前
[c语言]strcat函数的使用和模拟实现
c语言·开发语言·算法