一、基本术语

 

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

Vertex:图中的顶点;

 

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

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

 

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

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

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

 

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

 

无向完全图:每个顶点之间都有一条边的无向图;0<=边<=n(n-1)/2

有向完全图:每个顶点之间都有两条互为相反的边的无向图;0<=边<=n(n-1)

 

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

稠密图:边很多的图;

 

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

:带有权重的图;

 

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

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

路径的长度:是路径的边或者弧的数目

 

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

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

 

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

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

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

 

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

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

强连通分量:有向图中的极大连通子图 称之为;

 

一个连通图的生成树是 极小连通子图 类似于 树

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

                            如果一个图有 n 个顶点 n-1 个边,则一定是非连通图。 如果多于 n-1 个边则一定是个环!

 

如果一个有向图 有一个顶点入度为0 其余入度为1 则为有向树   

由图我们去掉若干边后生成森林

 

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

 

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

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

 

二、图的存储结构
   
     由于图顶点之间是多对多关系  都可能存在联系 ,因此 简单用顺序存储不能实现, 而用链式存储 因为 对应关系不知道 因此 会浪费很多next指针。
  
    但是前辈们还是提供了 五种不同的图存储结构

 

1.邻接矩阵

       图由顶点跟边或者弧构成  顶点不分大小主次  用一维数组表示顶点, 边或弧 用二维数组存储,二维数组就是邻接矩阵

       G(V,E) 如果有N个顶点 则临接矩阵为N*N 方阵

 

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

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

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

 图Graph_编程开发

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

 

2.邻接表

 

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

图Graph_编程开发_02

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

图Graph_编程开发_03

 

3.十字链表

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

顶点结点:

 

边结点:

 图Graph_Graph_04

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

4.邻接多重表

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

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

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

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

 

比如:

 图Graph_编程开发_05

 

5.边集数组

 

合依次对边进行操作;

存储边的信息,如下图:

图Graph_编程开发_06

 

三、图的遍历

 

 

DFS

 

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

比如:

图Graph_编程开发_07

 

[java]  view plain  copy
 
  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.   
  13. private void DFS_Traverse(Graph2 g, int i) {  
  14.     visited[i] = true;  
  15.     System.out.println(i);  
  16.     EdgeNode node = g.nodes[i].next;  
  17.     while (node != null) {  
  18.         if (!visited[node.idx]) {  
  19.             DFS_Traverse(g, node.idx);  
  20.         }  
  21.         node = node.next;  
  22.     }  
  23. }  

 

 

 

BFS

 

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

 

[java]  view plain  copy
 
  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.                         }  
  22.                         current = current.next;  
  23.                     }  
  24.                 }  
  25.   
  26.             }  
  27.         }  
  28.     }  

 

 

 

 

 

四、最小生成树
 

prim

 
邻接矩阵存储;

 

[java]  view plain  copy
 
  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.           
  10.         for(int i=0;i<g1.vertex.length;i++){  
  11.             cost[i] = g1.adjMatrix[0][i];  
  12.         }  
  13.         cost[0] = 0;  
  14.           
  15.         for(int i=1;i<g1.vertex.length;i++){  
  16.             int min = 65536;  
  17.             int k = 0;  
  18.             for(int j=1;j<g1.vertex.length;j++){  
  19.                 if(cost[j]!=0&&cost[j]<min){  
  20.                     min = cost[j];  
  21.                     k = j;  
  22.                 }  
  23.             }  
  24.             cost[k] = 0;  
  25.             System.out.println(pre[k]+","+k);  
  26.             for(int j=1;j<g1.vertex.length;j++){  
  27.                 if(cost[j]!=0&&g1.adjMatrix[k][j]<cost[j]){  
  28.                     pre[j] = k;  
  29.                     cost[j] = g1.adjMatrix[k][j];  
  30.                 }  
  31.             }  
  32.         }  
  33.     }  

 

 

 

krustral

边集数组存储;

 

 

[java]  view plain  copy
 
  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.     }  
  20.     private static int find(int[] parent, int f) {  
  21.         while (parent[f] > 0) {  
  22.             f = parent[f];  
  23.         }  
  24.         return f;  
  25.     }  

 

 

 

五、最短路径
 

 

dijkstra算法

 
邻接矩阵存储;
[java]  view plain  copy
 
  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.     }  
 

Floyd

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

 

[java]  view plain  copy
 
  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.         }  
  28.         System.out.println();  
  29.     }  

 

六、拓扑排序

 

 

使用数据结构

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

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

 

[java]  view plain  copy
 
  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.         }  
  26.         if(count<g2.nodes.length){  
  27.             return "error";  
  28.         }  
  29.         return "ok";  
  30.     }  

 

 

 

 

 

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

 

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