zoukankan      html  css  js  c++  java
  • 图(带权无向图)最小生成树

    带权图的邻接矩阵中无连接的值为无限大
    最小生成树的算法:从一个顶点出发找到其他顶点的所有的边,放入优先列队,找到权值最小的,把它和它所到达的顶点放入树的集合中。再以终点作为源点找到所有到其他顶点的边(不包括已放入树中的顶点),放入优先队列中,再从中取最小的把它
    到达的顶点放入树的集合中(最小生成树)。再以终点作为源点找到所有到其他顶点的边(不包括已放入树中的顶点),放到优先队列中,再从中取最小的把它和它所到达的顶点放入树的集合中,反复这样操作到全部顶点都放入到树中为止。
    除了图之外,我们还需要优先队列
    注意是无向有权图的最小生成树
    优先队列存放的是边这个对象。
    最小生成树与最短路径是不同的东西
    最小生成树返回的是一个顶点序列

    带权图结构:边,顶点,优先队列,图
    步骤:
    初始化当前顶点索引为0
    {标志当前顶点正在访问
    寻找边插入队列(需要判断是否存在相同终点的边,有的话需要删除和替换)
    获取队列中最小的边
    输出
    改变全局变量当前顶点的值
    }循环

    public class Edge {//
        public int srcVert;//存放源点的索引值
        public int destVert;//存放终点的索引值
        public int distance;//边的权值
        public Edge(int sv,int dv,int d) {//初始化边
            srcVert=sv;
            destVert=dv;
            distance=d;
            
        }
    
    }
    public class PriorityQ {//优先队列(优先队列中存放的是边对象)
        private final int SIZE=20;//队列的总长度(规定边最大的数量)
        private Edge[] queArray;//存放边对象的数组
        private int size;//当前边的数量
        public PriorityQ() {
            queArray=new Edge[SIZE]; //初始化数组
            size=0;//初始化边数量为0
        }
        //将边插入边数组中
        public void insert(Edge item) {//根据权值将边插入队列中
            int j;
            for(j=0;j<size;j++)//循环数组,找到插入边的位置
                if(item.distance>=queArray[j].distance)//如果找到要比姚插入的边权值大的边,就插入该边的后面
                    break;//记录边的位置,即j
            //插入(移动和插入)
            for(int k=size-1;k>=j;k--)//位置后移动
                queArray[k+1]=queArray[k];
            queArray[j]=item;//插入item边
            size++;//数据项增一
        }
        //删除值最小的边
        public Edge removeMin() {
            //因为是优先队列,插入的时候最小值都在索引最大的地方,所以直接拿数组最后一个
            return queArray[--size];//最后一个数的索引是size-1
        }
        //删除指定的边(n为queArray数组的索引值)
        public void removeN(int n) {
            for(int j=n;j<size-1;j++)//从数组中删除一个数(移动)
                queArray[j]=queArray[j+1];
            size--;
        }
        public Edge peekMin() {
            return queArray[size-1];
            //查看最小的边(对应权值最小)
        }
        public int size() {
            return size;
            //当前边的数量
        }
        //判断是否为空
        public boolean isEmpty() {
            return size==0;
        }
        //查看特定的边(n为queArray数组的索引)
        public Edge peekN(int n) {
            return queArray[n];
        }
        //寻找特定终点的边(findDex为边对象的destVert属性值)
        public int find(int findDex) {
            for(int j=0;j<size;j++)
                if(queArray[j].destVert==findDex)
                    return j;//找到了就返回边位置j
            
            return -1;//没找到就返回-1
        }
        
        
    
    }
    //图的顶点
    public class Vertex {
        public char label;//顶点的标识符
        public boolean isVisited;//顶点有无被访问的标志
        public Vertex(char lab) {//初始化顶点(属性)
            label=lab;
            isVisited=false;
            
        }
        
    
    }
    public class Graph {
        private final int MAX_VERTS=20;//最大顶点数
        private final int INFINITY=1000;//无限大的值,用于表示不连通的权值
        private Vertex[] vertexList;//顶点数组
        private int [][]adjMat;//顶点关系的领接矩阵(邻接矩阵的每行或者每列的位置跟顶点数组是对应的)
        private int nVerts;//当前顶点个数
        private int currentnVert;//标志当前顶点,该值为当前顶点索引值
        private PriorityQ  thePQ;//优先列队
        private int nTree;//最小生成树算法过程中,标志已访问的顶点数量(总共需要访问的个数是顶点的总数)
        public Graph() {//初始化图
            vertexList=new Vertex[MAX_VERTS]; //初始化顶点数组
            adjMat=new int [MAX_VERTS][MAX_VERTS] ;//初始化邻接矩阵
            for(int j=0;j<MAX_VERTS;j++)
                for(int i=0;i<MAX_VERTS;i++)
                    adjMat[i][j]=INFINITY;
            nVerts=0;//初始化当前顶点个数
            thePQ=new PriorityQ();//建立列队对象
            
            
        }
        //向顶点数组中添加顶点对象(lab为顶点对象的label属性值)
        public void addVertex(char lab) {
            vertexList[nVerts++]=new Vertex(lab);//建立lab对象,往数组内添加
        }
        //添加边(向邻接矩阵中改变权值)
        public void addEdge(int start,int end,int weight) {
            //因为是无向图所以(i,j)(j,i)都要添加1
            adjMat[start][end]=weight;
            adjMat[end][start]=weight;
        }
        //打印顶点数组,根据获取的顶点数组的下标值,打印顶点
        public void displayVertex(int v) {
            System.out.print(vertexList[v].label);
        }
        //最小生成树(输出顶点序列)
        public void mstw() {
            currentnVert=0;//当前顶点是索引为0的顶点
            while(nTree<nVerts-1) { //遍历顶点数组,要遍历nVerts-1次.当nTree为0的时候遍历第一次,所以当遍历nVerts-1次的时候,nTree为nVerts-2
                vertexList[currentnVert].isVisited=true;//访问当前顶点
                nTree++;//树中值加1(当前访问该顶点)
                for(int j=0;j<nVerts;j++) {//找出当前顶点的边(遍历数组,是否已经访问过,是否是连通的,是否是自己)
                    if(j==currentnVert) continue;//如果当前比较的是自己,退出此次操作,从j+1开始
                    if(vertexList[j].isVisited) continue;//如果比较的是已经访问过的,即已经加入最小生成树的序列中,退出此次操作,从j+1开始
                    int distance=adjMat[currentnVert][j];//在邻接矩阵中取当前顶点到邻接点的边值
                    if(distance==INFINITY) continue;//无连接
                    //如果前面都通过了,说明顶点对象数组中索引为j的是邻接点
                    //放入优先队列中(边)   该边的源点是一个全局变量currentnVert,不断在发生变化
                    //插入有两步,第一需要判断队列里面是否有以j为终点的边,有的话需要比较删除。第二如果没有的话,就直接插入
                    putInPQ(j,distance);//边值和该点(源点去哪了)
                }
                if(this.thePQ.size()==0) {//如果优先队列一个值也没有,说明没有边,无连接
                    System.out.println("图中无连接");
                    return ;
                }
                Edge theEdge=thePQ.removeMin();//如果都成功了,移除队列中最小的边(就是我们要找的)
                int sourceVert=theEdge.srcVert;//最小边的起点
                currentnVert=theEdge.destVert;//最小边的终点--下一个循环开始的源点
                //打印该边的起点和终点
                System.out.print(vertexList[sourceVert].label);
                System.out.print(vertexList[currentnVert].label+" ");
            }
            for(int j=0;j<nVerts;j++)
                vertexList[j].isVisited=false;
        }
        //边是newDist,终点是newVert放入队列中
        public void putInPQ(int newVert,int newDist) {
            int queueIndex=thePQ.find(newVert);//查找终点是newVert的边
            if(queueIndex!=-1) {//如果找到了(就是说如果列队中有虽然起点不同,但是终点相同的值,就需要小的替换大的边)
                Edge tempEdge=thePQ.peekN(queueIndex);//根据边索引查询边
                int oldDist=tempEdge.distance;
                if(oldDist>newDist) {//如果新的边权值小就需要删除旧的边
                    thePQ.removeN(queueIndex);//删除旧边
                    Edge theEdge=new Edge(currentnVert,newVert,newDist);//建立新边(新边的起始点是一个全局变量,从一开始就设定了)
                    thePQ.insert(theEdge);//插入新边,不能直接替换旧边,因为还要根据优先队列,比较权值
                    
                }
                
                
            }else {//如果没有找到
                Edge theEdge=new Edge(currentnVert,newVert,newDist);
                thePQ.insert(theEdge);
            }
        }
    
        
        
        
    }
    public class Test {
        public static void main(String[] agrs) {
            Graph theGraph=new Graph();//创建一个图
            theGraph.addVertex('A');//添加顶点
            theGraph.addVertex('B');//添加顶点
            theGraph.addVertex('C');//添加顶点
            theGraph.addVertex('D');//添加顶点
            theGraph.addVertex('E');//添加顶点
            theGraph.addVertex('F');//添加顶点
            theGraph.addEdge(0, 1,6);//添加边
            theGraph.addEdge(0, 3,4);//添加边
            theGraph.addEdge(1,2,10);//添加边
            theGraph.addEdge(1,3,7);//添加边
            theGraph.addEdge(1,4,7);//添加边
            theGraph.addEdge(2,3,8);//添加边
            theGraph.addEdge(2,4,5);//添加边
            theGraph.addEdge(2,5,6);//添加边
            theGraph.addEdge(3,4,12);//添加边
            theGraph.addEdge(4,5,7);//添加边
            theGraph.mstw();
            
        }
    
    }
  • 相关阅读:
    八数码问题--bfs
    子集生成——增量构造法
    回溯法
    子集生成——二进制法
    子集生成
    dfs 记忆搜索——注意剪枝方式
    pyhon---信息的爬取与提取---bs4,BeautifulSoup,re库
    008sudo用户管理
    007Linux在线升级yum
    006NFS与TFTP服务器
  • 原文地址:https://www.cnblogs.com/S-Mustard/p/7701650.html
Copyright © 2011-2022 走看看