【Java数据结构】最小生成树prim算法kruskal算法和并查集

一.prim算法

1.找到当前值最小的首结点(优先队列)
2.遍历所有邻居,更新他们的距离(只更新权重)(类似与dijkstra算法,更新距离时有区别)
3.当前结点出队

package LanQiao.MinDist;

import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;

//最小生成树普里姆算法
//1.找到当前值最小的首结点(优先队列)
//2.遍历所有邻居,更新他们的距离(只更新权重)
//3.当前结点出队
public class prim {
    static class vartex {
        String name;
        List<Edge> edges;
        int dist = INF;
        static final Integer INF = Integer.MAX_VALUE;
        boolean visited;

        public vartex(String name) {
            this.name = name;
        }
    }

    static class Edge {
        int weight;
        vartex linked;

        public Edge(int weight, vartex linked) {
            this.weight = weight;
            this.linked = linked;
        }
    }

    public static void main(String[] args) {
        vartex v1 = new vartex("v1");
        vartex v2 = new vartex("v2");
        vartex v3 = new vartex("v3");
        vartex v4 = new vartex("v4");
        vartex v5 = new vartex("v5");
        vartex v6 = new vartex("v6");
        vartex v7 = new vartex("v7");
        v1.edges = List.of(new Edge(2, v2), new Edge(1, v4), new Edge(4, v3));
        v2.edges = List.of(new Edge(3, v4), new Edge(2, v1), new Edge(10, v5));
        v3.edges = List.of(new Edge(4, v1), new Edge(2, v4), new Edge(5, v6));
        v4.edges = List.of(new Edge(1, v1), new Edge(3, v2), new Edge(2, v3), new Edge(8, v6),
                new Edge(4, v7), new Edge(7, v5));
        v5.edges = List.of(new Edge(10, v2), new Edge(7, v4), new Edge(6, v7));
        v6.edges = List.of(new Edge(5, v3), new Edge(8, v4), new Edge(1, v7));
        v7.edges = List.of(new Edge(1, v6), new Edge(4, v4), new Edge(6, v5));
        List<vartex> graph = List.of(v1, v2, v3, v4, v5, v6, v7);

        prim(graph, v1);


    }

    private static void prim(List<vartex> graph, vartex source) {
        PriorityQueue<vartex> queue = new PriorityQueue<>(Comparator.comparingInt(v -> v.dist));

        for (vartex v : graph) {
            queue.offer(v);//入队
        }


        source.dist = 0;
        while (!queue.isEmpty()) {
//            for (int i = 0; i < 6; i++) {
//                queue.offer(graph.get(i));
//            }
//            System.out.println(queue);

            //1.找到当前值最小的首结点
            vartex peek = queue.peek();
            //2.遍历所有邻居,更新他们的距离
            for (Edge edge : peek.edges) {
                vartex n = edge.linked;

                int dist = edge.weight;
                if (dist < n.dist)
                    n.dist = dist;
            }
            //3.当前结点出队
            vartex poll = queue.poll();
            poll.visited = true;
        }


        for (vartex v : graph) {
            System.out.println(v.name + " " + v.dist);


        }
    }
}

 二.kruskal

克鲁斯卡尔算法实现最小生成树

创建一个优先队列,把所有边加入,按照边权重大小依次出队,

并查集判断边的两段节点是否连通,连接后加入边集合

package LanQiao.MinDist;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
//克鲁斯卡尔算法实现最小生成树
//创建一个优先队列,把所有边加入,按照边权重大小依次出队,并查集判断边的两段节点是否连通,连接后加入边集合
public class Kruskal {
    static class vartex{
        String name;
        public vartex(String name) {
            this.name = name;
        }
    }

    static class Edge implements Comparable<Edge>{
        List<vartex> vartexes;
        int start;//代表索引
        int end;//代表索引
        int weight;

        public Edge(List<vartex> vartexes, int start, int end, int weight) {
            this.vartexes = vartexes;
            this.start = start;
            this.end = end;
            this.weight = weight;
        }

        //Comparable接口根据边的权重从小到大
        @Override
        public int compareTo(Edge o) {
            return Integer.compare(this.weight,o.weight);
        }
        @Override
        public  String toString(){

            return  vartexes.get(start).name+" "+vartexes.get(end).name+" "+weight;
        }

    }



    public static void main(String[] args) {
        vartex v1=new vartex("v1");
        vartex v2=new vartex("v2");
        vartex v3=new vartex("v3");
        vartex v4=new vartex("v4");
        vartex v5=new vartex("v5");
        vartex v6=new vartex("v6");
        vartex v7=new vartex("v7");
        List<vartex> vartexes=List.of(v1,v2,v3,v4,v5,v6,v7);//顶点集合
        PriorityQueue<Edge> queue=new PriorityQueue<>(List.of(//Edge实现Comparable接口,优先队列根据边的权重实现排序
                new Edge(vartexes,0,1,2),
                new Edge(vartexes,0,2,4),
                new Edge(vartexes,0,3,1),
                new Edge(vartexes,1,3,3),
                new Edge(vartexes,1 ,4,10),
                new Edge(vartexes,2 ,3,2),
                new Edge(vartexes, 2,5,5),
                new Edge(vartexes,3 ,4,7),
                new Edge(vartexes,3 ,5,8),
                new Edge(vartexes, 3,6,4),
                new Edge(vartexes, 4,6,6),
                new Edge(vartexes, 5,6,1)

        ));

     kruskal(queue,vartexes.size());
    }

    private static void kruskal(PriorityQueue<Edge> queue, int size) {
        List<Edge> edges=new ArrayList<>();//edges用来收集找到的边

        DisjointSet set =new DisjointSet(7);//并查集
        // 给元素之间建立一种关系,通过find和union方法实现

        while (edges.size()<size-1){
            Edge poll = queue.poll();
            int start = poll.start;
            int end = poll.end;
            int i = set.find(start);
            int j = set.find(end);
            if(start!=end){
                set.union(i,j);
                edges.add(poll);
            }

        }
       for (Edge edge:edges){
               System.out.println(edge.vartexes.get(edge.start).name+" "+edge.vartexes.get(edge.end).name+" "+edge.weight);
       }





    }

}

三.并查集 

并查集可以用来解决一些关于连通性的问题,例如在图论中判断节点之间是否连通,或者在计算机视觉中将图像的像素点划分成不同的区域

并查集包括两个主要操作:合并(Union)和查找(Find)。合并操作将两个集合合并为一个集合,查找操作用于查找某个元素所在的集合。并查集还可以使用路径压缩和按秩合并等优化策略来提高效率。

 

package LanQiao.MinDist;

import java.util.Arrays;
//并查集 find和union方法
//find 找出老大,值和下标相等
//union 在原来两个老大中选出新老夫,让两个集合相交
//find 优化 路径压缩
// public int  find(int x){
//        if(x==a[x]){
//            return x;
//        }
//        return a[x]=find(a[x]);
//    }
//union优化
//当有2个链接元素不等的集合需要连接时,让元素多的那一方当老大,提高整体查找老大的效率

public class DisjointSet {
    int a[];//记录元素老大关系,最开始初始化为自己
    int num[];//统计当前集合有多少元素相连接
    public DisjointSet(int size) {
        this.a = new int[size];
        this.num=new int[size];
        for (int i = 0; i < size; i++) {
            a[i]=i;
            num[i]=1;
        }
    }

    //并查集 find和union方法
 //找出老大,值和下标相等
        //find 优化 路径压缩
     public int  find(int x){
        if(x==a[x]){
            return x;
        }
        return a[x]=find(a[x]);
    }
    public void union(int x,int y){
        //在原来两个老大中选出新老夫,让两个集合相交

        //优化后
        if (num[x] > num[y]) {//x元素比y多,x当老大
            a[y]=x;
            num[x]=num[x]+num[y];//更新链接元素数目
        }
        else {
            a[x]=y;
            num[y]=num[y]+num[x];//更新连接元素数目
        }

    }





    public static void main(String[] args) {
        DisjointSet set=new DisjointSet(7);
       int i= set.find(0);
       int j= set.find(1);
//       if(i!=j){
//           set.union(i,j);
//       }
//       print(set);
//        int i1 = set.find(5);
//        int i2 = set.find(6);
//        if(i1!=i2) {
//            set.union(i1, i2);
//        }
//        print(set);
//        int i3=set.find(1);
//        int i4=set.find(5);
//        if(i3!=i4)
//            set.union(i3,i4);
//        print(set);
        //优化union
        set.union(0,1);//节点数相同,默认y是老大
        print(set);
        set.union(1,2);//2只有一个元素,1有2个元素,2指向1的老大
       print(set);
       //6只有一个元素,与1链接时,指向1的老大1
        //1的节点数为4
       set.union(1,6);
       print(set);


    }

    private static void print(DisjointSet set) {

        System.out.println("关系");
        for (int i = 0; i < set.a.length; i++) {
            System.out.print(set.a[i]+" ");

        }
        System.out.println();
        System.out.println("节点连接数目");
        for (int i = 0; i < set.num.length; i++) {
            System.out.print(set.num[i]+" ");
        }
        System.out.println();

    }


}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山河清风悠

你的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值