数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序

一、基本术语

图:由有穷、非空点集和边集合组成,简写成G(V,E);

Vertex:图中的顶点;

无向图:图中每条边都没有方向;

有向图:图中每条边都有方向;

无向边:边是没有方向的,写为(a,b)

有向边:边是有方向的,写为<a,b>

有向边也成为弧;开始顶点称为弧尾,结束顶点称为弧头;

简单图:不存在指向自己的边、不存在两条重复的边的图;

无向完全图:每个顶点之间都有一条边的无向图;

有向完全图:每个顶点之间都有两条互为相反的边的无向图;

稀疏图:边相对于顶点来说很少的图;

稠密图:边很多的图;

权重:图中的边可能会带有一个权重,为了区分边的长短;

网:带有权重的图;

度:与特定顶点相连接的边数;

出度、入度:对于有向图的概念,出度表示此顶点为起点的边的数目,入度表示此顶点为终点的边的数目;

环:第一个顶点和最后一个顶点相同的路径;

简单环:除去第一个顶点和最后一个顶点后没有重复顶点的环;

连通图:任意两个顶点都相互连通的图;

极大连通子图:包含竟可能多的顶点(必须是连通的),即找不到另外一个顶点,使得此顶点能够连接到此极大连通子图的任意一个顶点;

连通分量:极大连通子图的数量;

强连通图:此为有向图的概念,表示任意两个顶点a,b,使得a能够连接到b,b也能连接到a 的图;

生成树:n个顶点,n-1条边,并且保证n个顶点相互连通(不存在环);

最小生成树:此生成树的边的权重之和是所有生成树中最小的;

AOV网:结点表示活动的网;

AOE网:边表示活动的持续时间的网;

二、图的存储结构

1.邻接矩阵

维持一个二维数组,arr[i][j]表示i到j的边,如果两顶点之间存在边,则为1,否则为0;

维持一个一维数组,存储顶点信息,比如顶点的名字;

下图为一般的有向图:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

注意:如果我们要看vi节点邻接的点,则只需要遍历arr[i]即可;

下图为带有权重的图的邻接矩阵表示法:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

缺点:邻接矩阵表示法对于稀疏图来说不合理,因为太浪费空间;

2.邻接表

如果图示一般的图,则如下图:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

如果是网,即边带有权值,则如下图:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

3.十字链表

只针对有向图;,适用于计算出度和入度;

顶点结点:

边结点:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

好处:创建的时间复杂度和邻接链表相同,但是能够同时计算入度和出度;

4.邻接多重表

针对无向图; 如果我们只是单纯对节点进行操作,则邻接表是一个很好的选择,但是如果我们要在邻接表中删除一条边,则需要删除四个顶点(因为无向图);

在邻接多重表中,只需要删除一个节点,即可完成边的删除,因此比较方便;

因此邻接多重表适用于对边进行删除的操作;

顶点节点和邻接表没区别,边表节点如下图:

比如:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

5.边集数组

适合依次对边进行操作;

存储边的信息,如下图:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

三、图的遍历

DFS

思想:往深里遍历,如果不能深入,则回朔;

比如:

《数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序》

  1. /**
  2.  * O(v+e)
  3.  */
  4. @Test
  5. public void DFS() {
  6.     for (int i = 0; i < g.nodes.length; i++) {
  7.         if (!visited[i]) {
  8.             DFS_Traverse(g, i);
  9.         }
  10.     }
  11. }
  12. private void DFS_Traverse(Graph2 g, int i) {
  13.     visited[i] = true;
  14.     System.out.println(i);
  15.     EdgeNode node = g.nodes[i].next;
  16.     while (node != null) {
  17.         if (!visited[node.idx]) {
  18.             DFS_Traverse(g, node.idx);
  19.         }
  20.         node = node.next;
  21.     }
  22. }
    /**
     * O(v+e)
     */
    @Test
    public void DFS() {
        for (int i = 0; i < g.nodes.length; i++) {
            if (!visited[i]) {
                DFS_Traverse(g, i);
            }
        }
    }

    private void DFS_Traverse(Graph2 g, int i) {
        visited[i] = true;
        System.out.println(i);
        EdgeNode node = g.nodes[i].next;
        while (node != null) {
            if (!visited[node.idx]) {
                DFS_Traverse(g, node.idx);
            }
            node = node.next;
        }
    }

BFS

思想:对所有邻接节点遍历;

[java]
view plain
copy
print
?

  1. <span style=“white-space:pre”>  </span>/**
  2.      * O(v+e)
  3.      */
  4.     @Test
  5.     public void BFS() {
  6.         ArrayList<Integer> list = new ArrayList<Integer>();
  7.         for (int i = 0; i < g.nodes.length; i++) {
  8.             if (!visited[i]) {
  9.                 visited[i] = true;
  10.                 list.add(i);
  11.                 System.out.println(i);
  12.                 while (!list.isEmpty()) {
  13.                     int k = list.remove(0);
  14.                     EdgeNode current = g.nodes[k].next;
  15.                     while (current != null) {
  16.                         if (!visited[current.idx]) {
  17.                             visited[current.idx] = true;
  18.                             System.out.println(current.idx);
  19.                             list.add(current.idx);
  20.                         }
  21.                         current = current.next;
  22.                     }
  23.                 }
  24.             }
  25.         }
  26.     }
 /**
     * O(v+e)
     */
    @Test
    public void BFS() {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < g.nodes.length; i++) {
            if (!visited[i]) {
                visited[i] = true;
                list.add(i);
                System.out.println(i);
                while (!list.isEmpty()) {
                    int k = list.remove(0);
                    EdgeNode current = g.nodes[k].next;
                    while (current != null) {
                        if (!visited[current.idx]) {
                            visited[current.idx] = true;
                            System.out.println(current.idx);
                            list.add(current.idx);

                        }
                        current = current.next;
                    }
                }

            }
        }
    }

四、最小生成树

prim

邻接矩阵存储;

[java]
view plain
copy
print
?

  1. <span style=“white-space:pre”>  </span>/**
  2.      * 时间复杂度为O(n^2)
  3.      * 适用于稠密图
  4.      */
  5.     @Test
  6.     public void prim(){
  7.         int cost[] = new int[9];
  8.         int pre[] = new int[9];
  9.         for(int i=0;i<g1.vertex.length;i++){
  10.             cost[i] = g1.adjMatrix[0][i];
  11.         }
  12.         cost[0] = 0;
  13.         for(int i=1;i<g1.vertex.length;i++){
  14.             int min = 65536;
  15.             int k = 0;
  16.             for(int j=1;j<g1.vertex.length;j++){
  17.                 if(cost[j]!=0&&cost[j]<min){
  18.                     min = cost[j];
  19.                     k = j;
  20.                 }
  21.             }
  22.             cost[k] = 0;
  23.             System.out.println(pre[k]+”,”+k);
  24.             for(int j=1;j<g1.vertex.length;j++){
  25.                 if(cost[j]!=0&&g1.adjMatrix[k][j]<cost[j]){
  26.                     pre[j] = k;
  27.                     cost[j] = g1.adjMatrix[k][j];
  28.                 }
  29.             }
  30.         }
  31.     }
 /**
     * 时间复杂度为O(n^2)
     * 适用于稠密图
     */
    @Test
    public void prim(){
        int cost[] = new int[9];
        int pre[] = new int[9];

        for(int i=0;i<g1.vertex.length;i++){
            cost[i] = g1.adjMatrix[0][i];
        }
        cost[0] = 0;

        for(int i=1;i<g1.vertex.length;i++){
            int min = 65536;
            int k = 0;
            for(int j=1;j<g1.vertex.length;j++){
                if(cost[j]!=0&&cost[j]<min){
                    min = cost[j];
                    k = j;
                }
            }
            cost[k] = 0;
            System.out.println(pre[k]+","+k);
            for(int j=1;j<g1.vertex.length;j++){
                if(cost[j]!=0&&g1.adjMatrix[k][j]<cost[j]){
                    pre[j] = k;
                    cost[j] = g1.adjMatrix[k][j];
                }
            }
        }
    }

krustral

边集数组存储;

[java]
view plain
copy
print
?

  1. <span style=“white-space:pre”>  </span>/**
  2.      * 时间复杂度:O(eloge)
  3.      * 适用于稀疏图
  4.      */
  5.     @Test
  6.     public void krustral(){
  7.         Edge[] edges = initEdges();
  8.         int parent[] = new int[9];
  9.         for(int i=0;i<edges.length;i++){
  10.             Edge edge = edges[i];
  11.             int m = find(parent,edge.begin);
  12.             int n = find(parent,edge.end);
  13.             if(m!=n){
  14.                 parent[m] = n;
  15.                 System.out.println(m+”,”+n);
  16.             }
  17.         }
  18.     }
  19.     private static int find(int[] parent, int f) {
  20.         while (parent[f] > 0) {
  21.             f = parent[f];
  22.         }
  23.         return f;
  24.     }
 /**
     * 时间复杂度:O(eloge)
     * 适用于稀疏图
     */
    @Test
    public void krustral(){
        Edge[] edges = initEdges();
        int parent[] = new int[9];
        for(int i=0;i<edges.length;i++){
            Edge edge = edges[i];
            int m = find(parent,edge.begin);
            int n = find(parent,edge.end);
            if(m!=n){
                parent[m] = n;
                System.out.println(m+","+n);
            }
        }

    }
    private static int find(int[] parent, int f) {
        while (parent[f] > 0) {
            f = parent[f];
        }
        return f;
    }

五、最短路径

dijkstra算法

邻接矩阵存储;
[java]
view plain
copy
print
?

  1. <span style=“white-space:pre”>  </span>//O(n^2)
  2.     @Test
  3.     public void Dijkstra(){
  4.         int distance[] = new int[9];
  5.         int pre[] = new int[9];
  6.         boolean finished[] = new boolean[9];
  7.         finished[0] = true;
  8.         for(int i=0;i<9;i++){
  9.             distance[i] = g1.adjMatrix[0][i];
  10.         }
  11.         int k = 0;
  12.         for(int i=1;i<9;i++){
  13.             int min = 65536;
  14.             for(int j=0;j<9;j++){
  15.                 if(!finished[j]&&distance[j]<min){
  16.                     min = distance[j];
  17.                     k = j;
  18.                 }
  19.             }
  20.             finished[k] = true;
  21.             System.out.println(pre[k]+”,”+k);
  22.             for(int j=1;j<9;j++){
  23.                 if(!finished[j]&&(min+g1.adjMatrix[k][j])<distance[j]){
  24.                     distance[j] = min+g1.adjMatrix[k][j];
  25.                     pre[j] = k;
  26.                 }
  27.             }
  28.         }
  29.     }
 //O(n^2)
    @Test
    public void Dijkstra(){
        int distance[] = new int[9];
        int pre[] = new int[9];
        boolean finished[] = new boolean[9];
        finished[0] = true;
        for(int i=0;i<9;i++){
            distance[i] = g1.adjMatrix[0][i];
        }
        int k = 0;
        for(int i=1;i<9;i++){
            int min = 65536;
            for(int j=0;j<9;j++){
                if(!finished[j]&&distance[j]<min){
                    min = distance[j];
                    k = j;
                }
            }
            finished[k] = true;
            System.out.println(pre[k]+","+k);
            for(int j=1;j<9;j++){
                if(!finished[j]&&(min+g1.adjMatrix[k][j])<distance[j]){
                    distance[j] = min+g1.adjMatrix[k][j];
                    pre[j] = k;
                }
            }
        }
    }

Floyd

使用: (1)邻接矩阵:存储图;

[java]
view plain
copy
print
?

  1. <span style=“white-space:pre”>  </span>/**
  2.      * O(n^3)
  3.      * 求出任意顶点之间的距离
  4.      */
  5.     @Test
  6.     public void floyd(Graph1 g) {
  7.         int i, j, k;
  8.         int length = g.vertex.length;
  9.         int dist[][] = new int[length][length];
  10.         int pre[][] = new int[length][length];
  11.         for (i = 0; i < g.vertex.length; i++) {
  12.             for (j = 0; j < g.vertex.length; j++) {
  13.                 pre[i][j] = j;
  14.                 dist[i][j] = g.adjMatrix[i][j];
  15.             }
  16.         }
  17.         for (i = 0; i < length; i++) {
  18.             for (j = 0; j < g.vertex.length; j++) {
  19.                 for (k = 0; k < g.vertex.length; k++) {
  20.                     if (dist[i][j] > dist[i][k] + dist[k][j]) {
  21.                         dist[i][j] = dist[i][k] + dist[k][j];
  22.                         pre[i][j] = pre[i][k];
  23.                     }
  24.                 }
  25.             }
  26.         }
  27.         System.out.println();
  28.     }
 /**
     * O(n^3)
     * 求出任意顶点之间的距离
     */
    @Test
    public void floyd(Graph1 g) {
        int i, j, k;
        int length = g.vertex.length;
        int dist[][] = new int[length][length];
        int pre[][] = new int[length][length];
        for (i = 0; i < g.vertex.length; i++) {
            for (j = 0; j < g.vertex.length; j++) {
                pre[i][j] = j;
                dist[i][j] = g.adjMatrix[i][j];
            }
        }
        for (i = 0; i < length; i++) {
            for (j = 0; j < g.vertex.length; j++) {
                for (k = 0; k < g.vertex.length; k++) {
                    if (dist[i][j] > dist[i][k] + dist[k][j]) {
                        dist[i][j] = dist[i][k] + dist[k][j];
                        pre[i][j] = pre[i][k];
                    }
                }
            }

        }
        System.out.println();
    }

六、拓扑排序

使用数据结构

(1)栈:用来存放入度为0的节点;

(2)变种邻接列表:作为图的存储结构;此邻接列表的顶点节点还需要存放入度属性;

[java]
view plain
copy
print
?

  1. /**
  2. * O(n+e)
  3. */
  4. private static String topologicalSort(Graph2 g2) {
  5.         Stack<Integer> s = new Stack<Integer>();
  6.         int count = 0;
  7.         for(int i=0;i<g2.nodes.length;i++){
  8.             if(g2.nodes[i].indegree==0){
  9.                 s.push(i);
  10.             }
  11.         }
  12.         while(!s.isEmpty()){
  13.             int value = s.pop();
  14.             System.out.println(value+”、”);
  15.             count++;
  16.             EdgeNode node = g2.nodes[value].next;
  17.             while(node!=null){
  18.                 g2.nodes[node.idx].indegree–;
  19.                 if(g2.nodes[node.idx].indegree==0){
  20.                     s.push(node.idx);
  21.                 }
  22.                 node = node.next;
  23.             }
  24.         }
  25.         if(count<g2.nodes.length){
  26.             return “error”;
  27.         }
  28.         return “ok”;
  29.     }
/**
* O(n+e)
*/
private static String topologicalSort(Graph2 g2) {
        Stack<Integer> s = new Stack<Integer>();
        int count = 0;
        for(int i=0;i<g2.nodes.length;i++){
            if(g2.nodes[i].indegree==0){
                s.push(i);
            }
        }
        while(!s.isEmpty()){
            int value = s.pop();
            System.out.println(value+"、");
            count++;
            EdgeNode node = g2.nodes[value].next;
            while(node!=null){
                g2.nodes[node.idx].indegree--;
                if(g2.nodes[node.idx].indegree==0){
                    s.push(node.idx);
                }
                node = node.next;
            }

        }
        if(count<g2.nodes.length){
            return "error";
        }
        return "ok";
    }

七、关键路径

使用数据结构: (1)变种邻接列表:同拓扑排序; (2)变量: ltv表示某个事件的最晚开始时间; etv表示事件最早开始时间; ete表示活动最早开始时间; lte表示活动最晚开始时间;

[java]
view plain
copy
print
?

  1. <span style=“white-space:pre”>  </span>//O(n+e)
  2. <span style=”white-space:pre”>  </span>@Test
  3.     public void CriticalPath(){
  4.         Stack<Integer> stack = topological_etv();
  5.         int length = stack.size();
  6.         if(stack==null){
  7.             return ;
  8.         }
  9.         else{
  10.             int[]ltv = new int[length];
  11.             for(int i=0;i<stack.size();i++){
  12.                 ltv[i] = etv[stack.size()-1];
  13.             }
  14.             //从拓扑排序的最后开始计算ltv
  15.             while(!stack.isEmpty()){
  16.                 int top = stack.pop();
  17.                 EdgeNode current = g.nodes[top].next;
  18.                 while(current!=null){
  19.                     int idx = current.idx;
  20.                     //最晚发生时间要取所有活动中最早的
  21.                     if((ltv[idx]-current.weight)<ltv[top]){
  22.                         ltv[top] = ltv[idx]-current.weight;
  23.                     }
  24.                 }
  25.             }
  26.             int ete = 0;
  27.             int lte = 0;
  28.             for(int j=0;j<length;j++){
  29.                 EdgeNode current = g.nodes[j].next;
  30.                 while(current!=null){
  31.                     int idx = current.idx;
  32.                     ete = etv[j];
  33.                     lte = ltv[idx]-current.weight;
  34.                     if(ete==lte){
  35.                         //是关键路径
  36.                     }
  37.                 }
  38.             }
  39.         }
  40.     }
  41.     private Stack<Integer> topological_etv(){
  42.         Stack<Integer> stack2 = new Stack<Integer>();
  43.         Stack<Integer>stack1 = new Stack<Integer>();
  44.         for(int i=0;i<g.nodes.length;i++){
  45.             if(g.nodes[i].indegree==0){
  46.                 stack1.add(i);
  47.             }
  48.         }
  49.         etv[] = new int[g.nodes.length];
  50.         int count = 0;
  51.         while(!stack1.isEmpty()){
  52.             int top = stack1.pop();
  53.             count++;
  54.             stack2.push(top);
  55.             EdgeNode current = g.nodes[top].next;
  56.             while(current!=null){
  57.                 int idx = current.idx;
  58.                 if((–g.nodes[idx].indegree)==0){
  59.                     stack1.push(idx);
  60.                 }
  61.                 if((etv[top]+current.weight)>etv[idx]){
  62.                     etv[idx] = etv[top]+current.weight;
  63.                 }
  64.                 current = current.next;
  65.             }
  66.         }
  67.         if(count<g.nodes.length){
  68.             return null;
  69.         }
  70.         return stack2;
  71.     }
 //O(n+e)
 @Test
    public void CriticalPath(){

        Stack<Integer> stack = topological_etv();
        int length = stack.size();
        if(stack==null){
            return ;
        }
        else{
            int[]ltv = new int[length];
            for(int i=0;i<stack.size();i++){
                ltv[i] = etv[stack.size()-1];
            }
            //从拓扑排序的最后开始计算ltv
            while(!stack.isEmpty()){
                int top = stack.pop();
                EdgeNode current = g.nodes[top].next;
                while(current!=null){
                    int idx = current.idx;
                    //最晚发生时间要取所有活动中最早的
                    if((ltv[idx]-current.weight)<ltv[top]){
                        ltv[top] = ltv[idx]-current.weight;
                    }
                }
            }
            int ete = 0;
            int lte = 0;
            for(int j=0;j<length;j++){
                EdgeNode current = g.nodes[j].next;
                while(current!=null){
                    int idx = current.idx;
                    ete = etv[j];
                    lte = ltv[idx]-current.weight;
                    if(ete==lte){
                        //是关键路径
                    }
                }
            }

        }


    }
    private Stack<Integer> topological_etv(){
        Stack<Integer> stack2 = new Stack<Integer>();
        Stack<Integer>stack1 = new Stack<Integer>();
        for(int i=0;i<g.nodes.length;i++){
            if(g.nodes[i].indegree==0){
                stack1.add(i);
            }
        }
        etv[] = new int[g.nodes.length];
        int count = 0;
        while(!stack1.isEmpty()){
            int top = stack1.pop();
            count++;
            stack2.push(top);

            EdgeNode current = g.nodes[top].next;
            while(current!=null){
                int idx = current.idx;
                if((--g.nodes[idx].indegree)==0){
                    stack1.push(idx);
                }
                if((etv[top]+current.weight)>etv[idx]){
                    etv[idx] = etv[top]+current.weight;
                }
                current = current.next;
            }
        }
        if(count<g.nodes.length){
            return null;
        }
        return stack2;
    }
    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/a20180825/article/details/77017500
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞