图(带权无向图)最小生成树

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

带权图结构:边,顶点,优先队列,图
步骤:
初始化当前顶点索引为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();
        
    }

}

 

posted @ 2017-10-20 21:12  S-Mustard  阅读(5510)  评论(0编辑  收藏  举报