最小生成树(二)Prim算法

本文详细介绍了最小生成树的概念,重点讲解了Prim算法的原理和实现过程,包括切分定理、无向边、边加权无向图、索引小值优先队列的应用。通过实例分析了Prim算法的空间和时间复杂度,并探讨了基于二叉堆的优化方案。最后提到了Fibonacci堆和Chazelle算法作为进一步优化的选择。
摘要由CSDN通过智能技术生成

一、思想

1.1 基本概念

  • 加权无向图的生成树:一棵含有其所有顶点的无环连通子图。
  • 最小生成树(MST):一棵权值最小(树中所有边的权值之和)的生成树。

1.2 算法原理

1.2.1 切分定理
  • 切分定义:图的一种切分是将图的所有顶点分为两个非空且不重合的两个集合。横切边是一条连接两个属于不同集合的顶点的边。
  • 切分定理:在一幅加权图中,给定任意的切分,它的横切边中的权重最小者必然属于图的最小生成树。
1.2.2 算法原理

切分定理是解决最小生成树问题的所有算法的基础。切分定理再结合贪心算法思想,就可以最终落地实现最小生成树。

 

Prim算法原理:

一开始树中只有一个顶点,向它添加v-1条边,每次总是将下一条连接 “树中的顶点” 与 “不在树中的顶点” 且权重最小的边,加入树中。如下图,当我们将顶点v添加到树中时,可能使得w到最小生成树的距离更近了(然后遍历顶点v的领接链表即可)。

核心

使用一个索引优先队列,保存每个非树顶点w的一条边(将它与树中顶点连接起来的权重最小的边)。优先队列(小顶堆)的最小键即是权重最小的横切边的权重,而和它相关联的顶点V就是下一个将被添加到树中的顶点。

 

二、实现

2.1 无向边

  1 package study.algorithm.graph;
  2 
  3 import study.algorithm.base.StdOut;
  4 
  5 /***
  6  * @Description 无向边
  7  * @author denny.zhang
  8  * @date 2020/5/25 10:34 上午
  9  */
 10 public class Edge implements Comparable<Edge> {
 11 
 12     /**
 13      * 一个顶点
 14      */
 15     private final int v;
 16     /**
 17      * 另一个顶点
 18      */
 19     private final int w;
 20     /**
 21      * 权重
 22      */
 23     private final double weight;
 24 
 25     /**
 26      * Initializes an edge between vertices {
   @code v} and {
   @code w} of
 27      * the given {
   @code weight}.
 28      *
 29      * @param  v one vertex
 30      * @param  w the other vertex
 31      * @param  weight the weight of this edge
 32      * @throws IllegalArgumentException if either {
   @code v} or {
   @code w} 
 33      *         is a negative integer
 34      * @throws IllegalArgumentException if {
   @code weight} is {
   @code NaN}
 35      */
 36     public Edge(int v, int w, double weight) {
 37         if (v < 0) throw new IllegalArgumentException("vertex index must be a nonnegative integer");
 38         if (w < 0) throw new IllegalArgumentException("vertex index must be a nonnegative integer");
 39         if (Double.isNaN(weight)) throw new IllegalArgumentException("Weight is NaN");
 40         this.v = v;
 41         this.w = w;
 42         this.weight = weight;
 43     }
 44 
 45     /**
 46      * Returns the weight of this edge.
 47      *
 48      * @return the weight of this edge
 49      */
 50     public double weight() {
 51         return weight;
 52     }
 53 
 54     /**
 55      * 返回边的任意一个顶点
 56      *
 57      * @return either endpoint of this edge
 58      */
 59     public int either() {
 60         return v;
 61     }
 62 
 63     /**
 64      * 返回边的另一个顶点
 65      *
 66      * @param  vertex one endpoint of this edge
 67      * @return the other endpoint of this edge
 68      * @throws IllegalArgumentException if the vertex is not one of the
 69      *         endpoints of this edge
 70      */
 71     public int other(int vertex) {
 72         if      (vertex == v) return w;
 73         else if (vertex == w) return v;
 74         else throw new IllegalArgumentException("Illegal endpoint");
 75     }
 76 
 77     /**
 78      * Compares two edges by weight.
 79      * Note that {
   @code compareTo()} is not consistent with {
   @code equals()},
 80      * which uses the reference equality implementation inherited from {
   @code Object}.
 81      *
 82      * @param  that the other edge
 83      * @return a negative integer, zero, or positive integer depending on whether
 84      *         the weight of this is less than, equal to, or greater than the
 85      *         argument edge
 86      */
 87     @Override
 88     public int compareTo(Edge that) {
 89         return Double.compare(this.weight, that.weight);
 90     }
 91 
 92     /**
 93      * Returns a string representation of this edge.
 94      *
 95      * @return a string representation of this edge
 96      */
 97     public String toString() {
 98         return String.format("%d-%d %.5f", v, w, weight);
 99     }
100 
101     /**
102      * Unit tests the {
   @code Edge} data type.
103      *
104      * @param args the command-line arguments
105      */
106     public static void main(String[] args) {
107         Edge e = new Edge(12, 34, 5.67);
108         StdOut.println(e);
109         StdOut.println("任意一个顶点="+e.either());
110         StdOut.println("另一个顶点="+e.other(12));
111     }
112 }

如上图,初始化时构造了一个邻接表。Bag<Edge>[] adj,如下图。每条边有2个顶点,所以插入adj[]中2次。

 

2.2.边加权无向图

  1 package study.algorithm.graph;
  2 
  3 import study.algorithm.base.*;
  4 
  5 import java.util.NoSuchElementException;
  6 
  7 /***
  8  * @Description 边权重无向图
  9  * @author denny.zhang
 10  * @date 2020/5/25 10:50 上午
 11  */
 12 public class EdgeWeightedGraph {
 13     private static final String NEWLINE = System.getProperty("line.separator");
 14 
 15     /**
 16      * 顶点数
 17      */
 18     private final int V;
 19     /**
 20      * 边数
 21      */
 22     private int E;
 23     /**
 24      * 顶点邻接表,每个元素Bag代表:由某个顶点关联的边数组,按顶点顺序排列
 25      */
 26     private Bag<Edge>[] adj;
 27     
 28     /**
 29      * Initializes an empty edge-weighted graph with {
   @code V} vertices and 0 edges.
 30      *
 31      * @param  V the number of vertices
 32      * @throws IllegalArgumentException if {
   @code V < 0}
 33      */
 34     public EdgeWeightedGraph(int V) {
 35         if (V < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative");
 36         this.V = V;
 37         this.E = 0;
 38         adj = (Bag<Edge>[]) new Bag[V];
 39         for (int v = 0; v < V; v++) {
 40             adj[v] = new Bag<Edge>();
 41         }
 42     }
 43 
 44     /**
 45      * Initializes a random edge-weighted graph with {
   @code V} vertices and <em>E</em> edges.
 46      *
 47      * @param  V the number of vertices
 48      * @param  E the number of edges
 49      * @throws IllegalArgumentException if {
   @code V < 0}
 50      * @throws IllegalArgumentException if {
   @code E < 0}
 51      */
 52     public EdgeWeightedGraph(int V, int E) {
 53         this(V);
 54         if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
 55         for (int i = 0; i < E; i++) {
 56             int v = StdRandom.uniform(V);
 57             int w = StdRandom.uniform(V);
 58             double weight = Math.round(100 * StdRandom.uniform()) / 100.0;
 59             Edge e = new Edge(v, w, weight);
 60             addEdge(e);
 61         }
 62     }
 63 
 64     /**  
 65      * Initializes an edge-weighted graph from an input stream.
 66      * The format is the number of vertices <em>V</em>,
 67      * followed by the number of edges <em>E</em>,
 68      * followed by <em>E</em> pairs of vertices and edge weights,
 69      * with each entry separated by whitespace.
 70      *
 71      * @param  in the input stream
 72      * @throws IllegalArgumentException if {
   @code in} is {
   @code null}
 73      * @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range
 74      * @throws IllegalArgumentException if the number of vertices or edges is negative
 75      */
 76     public EdgeWeightedGraph(In in) {
 77         if (in == null) throw new IllegalArgumentException("argument is null");
 78 
 79         try {
 80             // 顶点数
 81             V = in.readInt();
 82             // 邻接表
 83             adj = (Bag<Edge>[]) new Bag[V];
 84             // 初始化邻接表,一个顶点对应一条链表
 85             for (int v = 0; v < V; v++) {
 86                 adj[v] = new Bag<Edge>();
 87             }
 88             // 边数
 89             int E = in.readInt();
 90             if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
 91             // 遍历每一条边
 92             for (int i = 0; i < E; i++) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值