Prim 算法

 

prim算法:

首先我们选择0顶点作为最小生成树的第一个顶点,

与最小生成树0的的边有四个分别是 0-4,0-7,0-2,0-6

用他们对应的顶点值作为索引存入最小优先队列中,用他们的权值作为索引值

因此可得

根据最小优先队列我们可以得到权值最小的值,此时找到0-7,因此我们将7进入树中

此时0和7组成了最小生成树,其他顶点又是一个集合,7也有了横切边

7-4 7-5 7-1 7-2 将这些顶点存入最小优先队列中,

由于第一次切分时 索引 4 和 索引 2 已经存入了值,此时应该比较 7-4 和0-4的权值, 7-2和0-2权值,哪个边小,索引留哪个边

此时最小索引中的值

 此时再次通过最小索引优先队列找到 7-1边0.19,因此将1进入最小生成树中,

此后不断重复,直至找到所有的顶点,最终结果此图的最小生成树了


public class IndexMinPriorityQueue<T extends Comparable<T>> {
    /**           item    [F,E,D,C,B,A]
     *                    0 1 2 3 4 5
     *            pq     [0,5,4,2,3,1] 此时pq是调整后台的堆
     *            qp     [0,1 2 3 4 5]
     *            如果交换 pq数组 如果 i=1,j=4 既让pq中的值进行替换
     *
     *
     */
    T[] item;
    int[] pq;//保存每个元素在item数组中的索引,pq数组需要堆排序
    int[] qp;//保存qp的逆序,pq的值作为索引,pq的索引作为值
    int N;
    public boolean less(int i ,int j){
        //因为交换的item数组,但传过来的是pq中的索引其实操作的是pq数组,但pq数组保存的item数组中索引,
        return item[pq[i]].compareTo(item[pq[j]]) < 0;
    }
    public void swap(int i,int j){
        //交换pq数组中的数据
        int temp = pq[i];
        pq[i] = pq[j];
        pq[j] = temp;
        // 再次更新qp中数据
        qp[pq[i]]=i;
        qp[pq[j]] = j;
        
    }
    //判断k对应的元素是否存在
    public boolean contains(int k){
        return qp[k]!=-1;
    }
    public IndexMinPriorityQueue(int capacity){
        this.item = (T[]) new Comparable[capacity+1];
        this.pq = new int[capacity+1];
        this.qp = new int[capacity+1];
        this.N = 0;
        //默认情况下队列中没有存储任何数据,让qp中的元素都为-1,及没有存储任何元素
        for (int i = 0; i < qp.length; i++) {
            qp[i] = -1;
        }
    }
    //往队列中插入一个元素,并关联索引i
    public void insert(int i,T t){
        //判断i是否被关联,如果已经被关联,则不让插入
        if(contains(i)) return ;
        //元素个数+1;
        N++;
        //把数据存储到对应的i位置
        item[i] = t;
        //把i存储到pq中
        pq[N] = i;
        //通过qp来记录pq中的i
        qp[i]=N;
        //通过堆上浮
        swim(N);
        
    }
    //删除队列中最小的元素,并返回改元素关联的索引
    public int delMin(){
        //获取最小元素关联的索引
        int minIndex = pq[1];
        //交换pq索引1处的值和做大索引处的值
        swap(1,N);
        //删除qp中对应的内容
        qp[pq[N]] = -1; 
        //删除pq中最大索引处的内容,即删除最小值
        pq[N]=-1;
        //删除item中对应的内容
        item[minIndex] = null;
        //元素个数减一
        N--;
        //下层调整
        sink(1);
        return minIndex;
    }
    //删除索引i关联的元素
    public void delete(int i){
        //找到i在pq中索引
        int k = qp[i];
        //交换pq中索引k处的值和索引N处的值
        swap(k,N);
        //删除qp中的内容
        qp[pq[N]] = -1;
        //删除pq中内容
        pq[N] = -1;
        //删除item中内容
        item[k] = null;
        //元素的数量-1
        N--;
        //堆的调整
        sink(k);
        swim(k);
    }
    //把索引i关联的元素修改为t
    public  void changeItem(int i,T t) {
        //修改item数组i位置的元素为t
        item[i] = t;
        //找到i在pq中出现的位置
        int k = qp[i];
        //堆调整
        sink(k);
        swim(k);
    }

    private void swim(int k) {
        while(k>1){
            if(less(k,k/2)){
                swap(k,k/2);
            }
            k = k/2;
        }
    }

    private void sink(int n) {
        int min = 0;
        while(2*n<=N){
            if(2*n+1<=N){
              if(less(2*n+1,2*n)){
                  min = 2 * n + 1;
              }else{
                  min = 2 * n;
              }
            }else{
                min = 2 * n;
            }
            if(less(n,min)){
                break;
            }else{
                swap(n,min);
            }
            n = min;
        }
    }

    public static void main(String[] args) {
        IndexMinPriorityQueue<String> indexQueue = new IndexMinPriorityQueue<>(5);
        indexQueue.insert(0,"A");
        indexQueue.insert(1,"C");
        indexQueue.insert(2,"F");
        //测试修改
           indexQueue.changeItem(2,"B");
        while(indexQueue.N!=0){
            int index = indexQueue.delMin();
            System.out.println(index+" ");
        }

        
    }
    public boolean isEmpty(){
        return N==0;
    }


}

 


public class EdgeWeightedGraph {
    private final int v;
    private int E;
    private Queue<Edge>[] adj;
    public EdgeWeightedGraph(int v) {
        //初始化顶点数量
        this.v = v;
        this.E = 0;
        this.adj = new Queue[v];
        for (int i = 0; i < adj.length; i++) {
            adj[i] = new Queue<Edge>();
        }
    }
    public int v(){
        return v;
    }
    public  int E(){
       return E;
    }
    public void addEge(Edge e){
        int v = e.either();
        int w = e.other(v);
        adj[v].enqueue(e);
        adj[w].enqueue(e);
        E++;
    }
    public Queue<Edge> adj(int v){
        return adj[v];
    }
    //获取无向图的所有边
    public Queue<Edge> eges(){
        Queue<Edge> allEdges = new Queue<>();
        //遍历图中的每一个顶点,找到该顶点的邻接表,邻接表中存储了该顶点关联的每一条边
        for (int i = 0; i < v; i++) {
            //遍历v顶点的邻接表,找到每一条和v关联的边
            for (Edge edge : adj(i)) {
                if (edge.other(v)<v) {
                    allEdges.enqueue(edge);
                }
            }
        }
        return allEdges;
    }
}

public class Edge implements Comparable<Edge> {
    private final int v;//顶点一
    private final int w;//顶点二
    private final double weight;//当前边的权重

    public Edge(int v, int w, double weight) {
        this.v = v;
        this.w = w;
        this.weight = weight;
    }
     public double weight() {
        return weight;
     }
     public int either() {
        return v;
     }
     public int other(int vertex ){
        if(vertex == v){
            return w;
        }else{
            return v;
        }
     }
    @Override
    public int compareTo(Edge that) {
        int cmp ;
        //如果当前边的权重值大,则让cmp = 1;
        if(this.weight>that.weight){
            cmp = 1;
        }else if(this.weight<that.weight){
            cmp = -1;
        }else {
            cmp = 0;
        }
        return cmp;
    }
}
 /**
     * 8
     * 16
     * 4 5 0.35
     * 4 7 0.37
     * 5 7 0.28
     * 0 7 0.16
     * 1 5 0.32
     * 0 4 0.38
     * 2 3 0.17
     * 1 7 0.19
     * 0 2 0.26
     * 1 2 0.36
     * 1 3 0.29
     * 2 7 0.34
     * 6 2 0.40
     * 3 6 0.52
     * 6 0 0.58
     * 6 4 0.93
     */

import com.example.demo.code10.queue.Queue;
import com.example.demo.study.tree.youxianqueue.IndexMinPriorityQueue;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class PrimAlgorithm {
    //索引代表顶点,值表示当前顶点和最小生成树的最短边
    private Edge[] edgeTo;
    //索引代表顶点,值表示当前顶点和最小生成树之间的最短边的权重
    private double[] distTo;
    //索引代表顶点,如果当前顶点已经在树中,则值为true
    private boolean [] marked;
    //存放树中顶点和非树中顶点的有效的横切边,生成树和非生成树的有效横切边不止一条,我们很容器去除权值较小的边
    private IndexMinPriorityQueue<Double> pq;

    public PrimAlgorithm(EdgeWeightedGraph G) {
        this.edgeTo = new Edge[G.v()];
        this.distTo = new double[G.v()];
        for (int i = 0; i < distTo.length; i++) {
            distTo[i] = Double.POSITIVE_INFINITY;
        }
        this.marked = new boolean[G.v()];
        //初始化pq
        pq = new IndexMinPriorityQueue<Double>(G.v());
        //默认让顶点0进入到树中,但是树中只有一个顶点0,  因此顶点默认没有和其他的顶点相连,所以distTo对应位置的值存储0.0
        //假的树0.0
        distTo[0]  = 0.0;
        pq.insert(0,0.0);
        //遍历索引最小优先队列,拿到 最小切边对应的顶点,把该顶点加入到最小生成树中
        while(!pq.isEmpty()){
                visit(G,pq.delMin());
        }
    }
      //将顶点v添加到最小生成树中更新数据
    private void visit(EdgeWeightedGraph g, int v) {
        //把顶点v添加到最小生成树中
        marked[v] = true;
        //更新数据
        for (Edge e : g.adj(v)) {
            //获取e边另外一个顶点(当前顶点是v)
            //判断另外一个顶点是不是已经在树中,如果在树中则不做任和处理,如果不在树中更新数据
            int w = e.other(v);
            if(marked[w]){
                continue;
            }
            //判断边e的权重是否小于从w顶点到树中已经存在的最小边的权重
            if (e.weight()<distTo[w]) {
                //更新数据
                edgeTo[w] = e;
                distTo[w] = e.weight();
                if(pq.contains(w)){
                    pq.changeItem(w,e.weight());
                }else{
                    pq.insert(w,e.weight());    
                }
            }
            
        }
    }
    //获取最小生成树的所有边
    public Queue<Edge> edges(){
      Queue<Edge> allEdges = new Queue<>();
        for (int i = 0; i < edgeTo.length; i++) {
            if(edgeTo[i]!=null){
                allEdges.enqueue(edgeTo[i]);
            }
        }
        return allEdges;
    }
    public static void main(String[] args) throws Exception {
        //创建图
      BufferedReader reader = new BufferedReader(new InputStreamReader(PrimAlgorithm.class.getClassLoader().getResourceAsStream("traffic_project.txt")));
        int total = Integer.parseInt(reader.readLine());
        EdgeWeightedGraph G = new EdgeWeightedGraph(total);
        int edgesNumber = Integer.parseInt(reader.readLine());
        for (int e = 0; e < edgesNumber; e++) {
            String[] strs = reader.readLine().split(" ");
            int v = Integer.parseInt(strs[0]);
            int w = Integer.parseInt(strs[1]);
            double weight = Double.parseDouble(strs[2]);
            //构建无向边
            Edge edge = new Edge(v, w, weight);
            G.addEge(edge);
        }
        //生成最小生成树
        PrimAlgorithm primAlgorithm = new PrimAlgorithm(G);
        Queue<Edge> edges = primAlgorithm.edges();
        for (Edge edge : edges) {
            int v = edge.either();
            int w = edge.other(v);
            double weight = edge.weight();
            System.out.println(v+"-"+w+"::"+weight);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值