邻接表 prime算法_最小生成树 克鲁斯卡尔(Kruskal)算法

本文详细介绍了克鲁斯卡尔(Kruskal)算法,用于找到无向图的最小生成树。该算法首先将边按权重排序,然后通过并查集数据结构判断添加边是否会形成环路,以避免环路的出现。在逐步添加边的过程中,确保了最小生成树的构建。文章包括算法要点、图解示例和代码实现。
摘要由CSDN通过智能技术生成

8a8d100c5968e71557a0972bfb5cc44b.png

克鲁斯卡尔(Kruskal)算法

克鲁斯卡尔(Kruskal)算法 是求最小生成树的 了解最小生成树

普利姆(Prime)算法是以 为基础,挑选与 已有点 相连的最小边。

克鲁斯卡尔(Kruskal)算法是以 为基础,先将边从小到大排列,从小到大的添加不构成「环路」的边。

判断「环路」使用的是 并查集 的数据结构 学习并查集

1.要点

  • 核心:先将边从小到大排列,从小到大的添加不构成「环路」的边
  • 判断「环路」:使用 并查集 的数据结构 学习并查集
  • 时间复杂度:O(n log n) (这是由排序方式实现的)

2.图解

求下面图的最小生成树

0266fd62ee3791a8375f157df8a80221.png

首先将边从小到大排列 如下表所示。

| 边 | from | to | weight | | ---------- | ---- | ---- | ------ | | edges[0] | B | C | 2 | | edges[1] | A | C | 3 | | edges[2] | A | B | 3 | | edges[3] | B | D | 4 | | edges[4] | C | D | 6 | | edges[5] | A | D | 7 |

选取最小的边 BC,

  1. 通过 并查集 find() 找到 root 根节点,判断 B、C 不为同一颗子树
  2. 通过 并查集 union() 将 B、C 合并为一颗树

324672c8172efc061045b875fe789b65.png

选取第二小的边 AC,

  1. 通过 并查集 find() 找到 root 根节点,判断 A、C 不为同一颗子树
  2. 通过 并查集 union() 将 A、{'B','C'} 合并为一颗树

5eb971241aa870951a5d14c7e944c825.png

选取第三小的边 AB,

  1. 通过 并查集 find() 找到 root 根节点,判断 A、B 为同一颗子树(被红色线连接的点)
  2. 不进行合并操作

选取第四小的边 BD,

  1. 通过 并查集 find() 找到 root 根节点,判断 B、D 不为同一颗子树
  2. 通过 并查集 union() 将 D、{'A',B','C'} 合并为一颗树

65cc721464dac4bd543e9e33d6c86edd.png

我们得到了这个无向图的 最小生成树 (如上图所示)

3.代码

public ArrayList<Integer> kruskal(){
  //已经连接的点
  ArrayList<Integer> connectedVertices = new ArrayList<>();
  //获取图中的所有的边
  List<Edge> edges = getAllEdges();
  //将所有的边进行排序
  sortEdges(edges);
  //这里使用并查集寻找出最小生成树
  for (int i = 0; i < edges.size(); i++) {
    int fromVertexIndex = edges.get(i).from;
    int toVertexIndex = edges.get(i).to;
    boolean sameTree = find(fromVertexIndex) == find(toVertexIndex);
    //如果不是同一个树
    if (sameTree == false){
      System.out.println(vertices[fromVertexIndex] + " --> " + vertices[toVertexIndex] + " weight:" +graph[fromVertexIndex][toVertexIndex]);
      //合并为同一个树
      union(find(fromVertexIndex),find(toVertexIndex));
      //包含 to 点就添加 from 点
      if (connectedVertices.contains(toVertexIndex)) {
        connectedVertices.add(fromVertexIndex);
      }
      //包含 from 点 就添加 to 点
      else if (connectedVertices.contains(fromVertexIndex)){
        connectedVertices.add(toVertexIndex);
      }
      // 2 个都不包含就都添加
      else {
        connectedVertices.add(fromVertexIndex);
        connectedVertices.add(toVertexIndex);
      }
    }
    //如果全部点都已经连接就退出循环
    if (connectedVertices.size() == vertices.length){
      break;
    }
  }
  return connectedVertices;
}
例如 getAllEdges()sortEdges()find()union() 个方法上面就不赘述了,你可以 点击连接查看完整 Kruskal 代码

测试效果

B --> C weight:2
A --> C weight:3
B --> D weight:4克鲁斯卡尔(Kruskal)算法
普利姆(Prime)算法,克鲁斯卡尔(Kruskal)算法 是求最小生成树的,阅读之前请先了解
了解最小生成树
了解并查集

普利姆(Prime)算法 是以 为基础,挑选与 已有点 相连的最小边。

克鲁斯卡尔(Kruskal)算法是以 为基础,先将边从小到大排列,从小到大的添加不构成「环路」的边。

判断「环路」使用的是 并查集 的数据结构

1.要点

  • 核心:先将边从小到大排列,从小到大的添加不构成「环路」的边
  • 判断「环路」:使用 并查集 的数据结构
  • 时间复杂度:O(n logn) (这是由排序方式实现的)

2.图解

求下面图的最小生成树

0266fd62ee3791a8375f157df8a80221.png

首先将边从小到大排列 如下表所示。

310f287ad55b404ba48d5857a00e8722.png

选取最小的边 BC,

  1. 通过 并查集 find() 找到 root 根节点,判断 B、C 不为同一颗子树
  2. 通过 并查集 union() 将 B、C 合并为一颗树

324672c8172efc061045b875fe789b65.png

选取第二小的边 AC,

  1. 通过 并查集 find() 找到 root 根节点,判断 A、C 不为同一颗子树
  2. 通过 并查集 union() 将 A、{'B','C'} 合并为一颗树

5eb971241aa870951a5d14c7e944c825.png

选取第三小的边 AB,

  1. 通过 并查集 find() 找到 root 根节点,判断 A、B 为同一颗子树(被红色线连接的点)
  2. 不进行合并操作

选取第四小的边 BD,

  1. 通过 并查集 find() 找到 root 根节点,判断 B、D 不为同一颗子树
  2. 通过 并查集 union() 将 D、{'A',B','C'} 合并为一颗树

65cc721464dac4bd543e9e33d6c86edd.png

我们得到了这个无向图的 最小生成树 (如上图所示)

3.代码

/** 顶点 */
private static Character[] vertices;
/** Graph 邻接矩阵 */
private static int[][] graph;
/** 定义最大值 */
private static final int MAX = Integer.MAX_VALUE;
//初始化数据
static{
  vertices = new Character[]{'A', 'B', 'C', 'D'};
  graph = new int[][]{
    {0,3,3,7}
    ,{3,0,2,4}
    ,{3,2,0,6}
    ,{7,4,6,0}};
};
/** 定义边 Edge */
class Edge  implements Comparator<Edge> {
  int from;
  int to;
  int weight;
  public Edge(int from, int to, int weight) {
    this.from = from;
    this.to = to;
    this.weight = weight;
  }
}
/** kruskal */
public ArrayList<Integer> kruskal(){
  //已经连接的点
  ArrayList<Integer> connectedVertices = new ArrayList<>();
  //获取图中的所有的边
  List<Edge> edges = getAllEdges();
  //将所有的边进行排序
  sortEdges(edges);
  //这里使用并查集寻找出最小生成树
  for (int i = 0; i < edges.size(); i++) {
    int fromVertexIndex = edges.get(i).from;
    int toVertexIndex = edges.get(i).to;
    boolean sameTree = find(fromVertexIndex) == find(toVertexIndex);
    //如果不是同一个树
    if (sameTree == false){
      System.out.println(vertices[fromVertexIndex] + " --> " + vertices[toVertexIndex] + " weight:" +graph[fromVertexIndex][toVertexIndex]);
      //合并为同一个树
      union(find(fromVertexIndex),find(toVertexIndex));
      //包含 to 点就添加 from 点
      if (connectedVertices.contains(toVertexIndex)) {
        connectedVertices.add(fromVertexIndex);
      }
      //包含 from 点 就添加 to 点
      else if (connectedVertices.contains(fromVertexIndex)){
        connectedVertices.add(toVertexIndex);
      }
      // 2 个都不包含就都添加
      else {
        connectedVertices.add(fromVertexIndex);
        connectedVertices.add(toVertexIndex);
      }
    }
    //如果全部点都已经连接就退出循环
    if (connectedVertices.size() == vertices.length){
      break;
    }
  }
  return connectedVertices;
}
getAllEdges()sortEdges()find()union() 个方法上面就不赘述了,你可以
点击连接查看完整Kruskal代码

测试效果

B --> C weight:2
A --> C weight:3
B --> D weight:4
总结:
Kruskal算法先将所有的边进行排序
然后依次添加边上的节点(但是不能形成「环路」)
判断是否为「环路」使用的是 并查集 的数据结构
拓展
克鲁斯卡尔(Kruskal)算法属于贪心算法的一种(贪心算法还有 Kruskal克鲁斯克拉算法、Dijkstra迪克斯特拉算法、哈夫曼树、编码算法等)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值