数据结构-带权图

带权图的最小生成树

规则:总是选择权值最低的边。

带权图的最小生成树中有所有的顶点和连接它们的必要的边,且这些边的权值最小。

算法要点

从一个顶点开始,把它放入树的集合中。以下是步骤:

  1. 找到从最新的顶点到其他顶点的所有边,这些顶点不能在树的集合中。把这些边放入优先级队列。
  2. 找出权值最小的边,把它和它所到达的顶点放入树的集合中。

重复以上步骤,直到所有顶点都在树的集合中。

Java实现

package com.jikefriend.graph;

public class GraphMstw {
    static 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;
        }
    }

    /**
     * 优先级队列
     */
    static class PriorityQ {
        private final int SIZE = 20;
        private Edge[] queArray;
        private int size;

        public PriorityQ() {
            queArray = new Edge[SIZE];
            size = 0;
        }

        public void insert(Edge item) {
            int j;

            for (j = 0; j < size; j++)
                if (item.distance >= queArray[j].distance)
                    break;
            for (int i = size - 1; i >= j; i--)
                queArray[i + 1] = queArray[i];

            queArray[j] = item;
            size++;
        }

        public Edge removeMin() {
            return queArray[--size];
        }

        public void removeN(int n) {
            for (int i = n; i < size - 1; i++)
                queArray[i] = queArray[i + 1];
            size--;
        }

        public Edge peekMin() {
            return queArray[size - 1];
        }

        public int size() {
            return size;
        }

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

        public Edge peekN(int n) {
            return queArray[n];
        }

        public int find(int findDex) {
            for (int i = 0; i < size; i++)
                if (queArray[i].destVert == findDex)
                    return i;
            return -1;
        }
    }

    static class Vertex {
        public char label;
        public boolean isInTree;

        public Vertex(char lab) {
            label = lab;
            isInTree = false;
        }
    }

    static class Graph {
        private final int MAX_VERTS = 20;
        private final int INFINITY = 1000000;
        private Vertex[] vertexList;
        private int[][] adjMat;
        private int nVerts;
        private int currentVert;
        private PriorityQ thePQ;
        private int nTree;

        public Graph() {
            vertexList = new Vertex[MAX_VERTS];
            adjMat = new int[MAX_VERTS][MAX_VERTS];
            nVerts = 0;
            for (int i = 0; i < MAX_VERTS; i++)
                for (int j = 0; j < MAX_VERTS; j++)
                    adjMat[i][j] = INFINITY;
            thePQ = new PriorityQ();
        }

        public void addVertex(char lab) {
            vertexList[nVerts++] = new Vertex(lab);
        }

        public void addEdge(int start, int end, int weight) {
            adjMat[start][end] = weight;
            adjMat[end][start] = weight;
        }

        public void mstw() {
            currentVert = 0;

            /** 所有顶点都在树中则结束循环 */
            while (nTree < nVerts - 1) {
                /** 当前顶点放在书中 */
                vertexList[currentVert].isInTree = true;
                nTree++;

                /** 遍历所有顶点,将和这个顶点连接的边放到优先级队列中 */
                for (int i = 0; i < nVerts; i++) {
                    if (i == currentVert)       //源点和终点相同,跳过
                        continue;
                    if (vertexList[i].isInTree) //终点如果已经在树中,跳过
                        continue;
                    int distance = adjMat[currentVert][i];
                    if (distance == INFINITY)   //如果两个顶点直接没有边,跳过
                        continue;
                    putInPQ(i, distance);
                }
                if (thePQ.size() == 0) {        //如果优先级队列为空,则直接返回,表明图没有边
                    System.out.println("GRAPH NOT CONNECTED");
                    return;
                }
                /** 从队列中移除权值最小的边 */
                Edge theEdge = thePQ.removeMin();
                int sourceVert = theEdge.srcVert;
                /** 这条边的目的顶点变成当前顶点 */
                currentVert = theEdge.destVert;

                System.out.print(vertexList[sourceVert].label);
                System.out.print(vertexList[currentVert].label);
                System.out.print(" ");
            }
            /** 复位标识 */
            for (int i = 0; i < nVerts; i++)
                vertexList[i].isInTree = false;
        }

        public void putInPQ(int newVert, int newDist) {
            int queueIndex = thePQ.find(newVert);

            /** 如果队列中,特定目标顶点的边已经有一条 */
            if (queueIndex != -1) {
                Edge tempEdge = thePQ.peekN(queueIndex);
                int oldDist = tempEdge.distance;
                /** 如果新边有更小的权值,则把老边从队列中删除,把新边放进去 */
                if (oldDist > newDist) {
                    thePQ.removeN(queueIndex);
                    Edge theEdge = new Edge(currentVert, newVert, newDist);
                    thePQ.insert(theEdge);
                }
            } else {
                Edge theEdge = new Edge(currentVert, newVert, newDist);
                thePQ.insert(theEdge);
            }
        }
    }

    public static void main(String[] args) {
        Graph graph = new Graph();

        graph.addVertex('A');
        graph.addVertex('B');
        graph.addVertex('C');
        graph.addVertex('D');
        graph.addVertex('E');
        graph.addVertex('F');

        graph.addEdge(0, 1, 6);   //AB 6
        graph.addEdge(0, 3, 4);   //AD 4
        graph.addEdge(1, 2, 10);  //BC 10
        graph.addEdge(1, 3, 7);   //BD 7
        graph.addEdge(1, 4, 7);   //BE 7
        graph.addEdge(2, 3, 8);   //CD 8
        graph.addEdge(2, 4, 5);   //CE 5
        graph.addEdge(2, 5, 6);   //CF 6
        graph.addEdge(3, 4, 12);  //DE 12
        graph.addEdge(4, 5, 7);   //EF 7

        System.out.print("Mininum spanning tree: ");
        graph.mstw();
        System.out.println();
    }
}

最短路径问题

最短路径问题可以描述为:对给定的源点和终止点,找出权值最低的一条路线。

最短路径算法的关键是记录源点到其他顶点的最短路径,同时还要记录走过的路径。

无权图的最短路径问题要找到两点间的路径,且路径长度最短。

带权图的最短路径问题产生的路径总权值和最小。

Java实现

package com.jikefriend.graph;

public class GraphPath {

    /**
     * 用于保存源点到其他顶点的距离,已经顶点的父顶点(用于记录走过的路径)
     */
    static class DistPar {
        /** 起点到其他顶点的距离 */
        public int distance;
        /** 顶点的当前父顶点 */
        public int parentVert;

        public DistPar(int pv, int d) {
            distance = d;
            parentVert = pv;
        }
    }

    static class Vertex {
        public char label;
        public boolean isInTree;

        public Vertex(char lab) {
            label = lab;
            isInTree = false;
        }
    }

    static class Graph {
        private final int MAX_VERTS = 20;
        private final int INFINITY = 1000000;
        /** 顶点数组 */
        private Vertex[] vertexList;
        /** 邻接矩阵 */
        private int[][] adjMat;
        /** 顶点数量 */
        private int nVerts;
        /** 树中顶点的数量 */
        private int nTree;
        /** 总是存储从源点到所有已知顶点的当前最短路径 */
        private DistPar[] sPath;
        /** 当前顶点 */
        private int currentVert;
        /** 源点到当前顶点的权值(距离) */
        private int startToCurrent;

        public Graph() {
            vertexList = new Vertex[MAX_VERTS];
            adjMat = new int[MAX_VERTS][MAX_VERTS];
            nVerts = 0;
            nTree = 0;
            for (int i = 0; i < MAX_VERTS; i++)
                for (int j = 0; j < MAX_VERTS; j++)
                    adjMat[i][j] = INFINITY;
            sPath = new DistPar[MAX_VERTS];
        }

        public void addVertex(char lab) {
            vertexList[nVerts++] = new Vertex(lab);
        }

        public void addEdge(int start, int end, int weight) {
            adjMat[start][end] = weight;
        }

        public void path() {
            int startTree = 0;     //从起点开始
            vertexList[startTree].isInTree = true; //将起点放入树中
            nTree = 1;

            /** 把邻接矩阵的对应边的权值放入sPath[]数组中 */
            for (int i = 0; i < nVerts; i++) {
                int tempDist = adjMat[startTree][i];
                sPath[i] = new DistPar(startTree, tempDist);
            }

            while (nTree < nVerts) {
                /** 选择最小权值 */
                int indexMin = getMin();
                int minDist = sPath[indexMin].distance;

                if (minDist == INFINITY) {
                    System.out.println("There are unreachable vertices");
                    break;
                } else {
                    /** 如果最小权值的顶点从源点可达,则更新当前顶点 */
                    currentVert = indexMin;
                    /** sPath[]数组的当前项的权值 */
                    startToCurrent = sPath[indexMin].distance;
                }
                /** 把最小权值对应的顶点放入树中 */
                vertexList[currentVert].isInTree = true;
                nTree++;
                /** 根据当前顶点的变化更新sPath[]数组 */
                adjust_sPath();
            }

            display();

            nTree = 0;
            for (int i = 0; i < nVerts; i++)
                vertexList[i].isInTree = false;
        }

        private int getMin() {
            int minDist = INFINITY;
            int indexMin = 0;
            for (int i = 0; i < nVerts; i++) {
                if (!vertexList[i].isInTree && sPath[i].distance < minDist) {
                    minDist = sPath[i].distance;
                    indexMin = i;
                }
            }
            return indexMin;
        }

        /**
         * 更新sPath[]数组
         */
        private void adjust_sPath() {
            int column = 1;
            while (column < nVerts) {
                /** 如果已经在树中,跳至下一顶点 */
                if (vertexList[column].isInTree) {
                    column++;
                    continue;
                }
                /** 获取到当前顶点的权值 */
                int currentToFringe = adjMat[currentVert][column];
                /** 获取路过路径的总权值 */
                int startToFringe = startToCurrent + currentToFringe;
                /** 获取sPath[]数组当前项的权值 */
                int sPathDist = sPath[column].distance;

                /** 如果startToFringe更小,则替换 */
                if (startToFringe < sPathDist) {
                    sPath[column].parentVert = currentVert;
                    sPath[column].distance = startToFringe;
                }
                column++;
            }
        }

        public void display() {
            for (int i = 0; i < nVerts; i++) {
                System.out.print(vertexList[i].label + "=");
                if (sPath[i].distance == INFINITY)
                    System.out.print("inf");
                else
                    System.out.print(sPath[i].distance);
                char parent = vertexList[sPath[i].parentVert].label;
                System.out.print("(" + parent + ") ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Graph graph = new Graph();

        graph.addVertex('A');
        graph.addVertex('C');
        graph.addVertex('B');
        graph.addVertex('D');
        graph.addVertex('E');

        graph.addEdge(0, 1, 50);   //AB 50
        graph.addEdge(0, 3, 80);   //AD 80
        graph.addEdge(1, 2, 60);   //BC 60
        graph.addEdge(1, 3, 90);   //BD 90
        graph.addEdge(2, 4, 40);   //CE 40
        graph.addEdge(3, 2, 20);   //DC 20
        graph.addEdge(3, 4, 70);   //DE 70
        graph.addEdge(4, 1, 50);   //EB 50

        System.out.println("Shortest paths");
        graph.path();
        System.out.println();
    }
}



摘自《Java数据结构算法(第二版)》 [美] Robert Lafore 著

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值