图论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)));
 }
相关推荐
pianmian11 小时前
python数据结构基础(7)
数据结构·算法
好奇龙猫3 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
sp_fyf_20244 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
香菜大丸4 小时前
链表的归并排序
数据结构·算法·链表
jrrz08284 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time4 小时前
golang学习2
算法
南宫生5 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步6 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
Ni-Guvara6 小时前
函数对象笔记
c++·算法
泉崎6 小时前
11.7比赛总结
数据结构·算法