有向图

有向图同无向图的区别为每条边带有方向,表明从一个顶点至另一个顶点可达。有向图的算法多依赖深度搜索算法。

本文主要介绍有向图的基本算法,涉及图的表示、可达性、检测环、图的遍历、拓扑排序以及强连通检测等算法。

1 定义有向图

采用邻接表结构存储边信息,同时提供reverse接口生成反向图,倒置每个边的方向,该接口在后续其他算法中会用到。

  1. /**
  2. * 采用邻接表表示的有向图
  3. */
  4. public class DiGraph {
  5. private final int V;
  6. private int E;
  7. private ArrayList<Integer>[] adj;
  8. public DiGraph(int V)
  9. {
  10. this.V = V;
  11. E = 0;
  12. adj = new ArrayList[V];
  13. for (int i = 0; i < V; i++) {
  14. adj[i] = new ArrayList<>();
  15. }
  16. }
  17. public DiGraph(Scanner scanner)
  18. {
  19. this(scanner.nextInt());
  20. int E = scanner.nextInt();
  21. for (int i = 0; i < E; i++) {
  22. int v = scanner.nextInt();
  23. int w = scanner.nextInt();
  24. addEdge(v, w);
  25. }
  26. }
  27. public void addEdge(int v, int w)
  28. {
  29. // 添加一条v指向w的边
  30. adj[v].add(w);
  31. E++;
  32. }
  33. /**
  34. * 返回有向图的反向图, 将每条边的方向反转
  35. */
  36. public DiGraph reverse()
  37. {
  38. DiGraph diGraph = new DiGraph(V);
  39. for (int v = 0; v < V; v++) {
  40. for (int w : adj[v]) {
  41. diGraph.addEdge(w, v);
  42. }
  43. }
  44. return diGraph;
  45. }
  46. public void show() {
  47. System.out.println("V: " + V);
  48. System.out.println("E: " + E);
  49. for (int i = 0; i < V; i++) {
  50. System.out.print(i + ": ");
  51. for (Integer integer : adj[i]) {
  52. System.out.print(integer + " ");
  53. }
  54. System.out.println();
  55. }
  56. }
  57. public static void main(String[] args) {
  58. Scanner scanner = new Scanner(System.in);
  59. // 输入用例参加附录1
  60. DiGraph diGraph = new DiGraph(scanner);
  61. // 输入结果见附录2
  62. diGraph.show();
  63. }
  64. }

2 有向图的可达性

有向图的可达性是指给定一个或一组顶点,判断是否可以到达图中其他顶点。垃圾清除常见算法“标记-清除”算法中,采用有向图的可达性算法

标记所有可以被访问的对象,然后在回收阶段,仅仅回收那些未被标记的对象。

  1. /**
  2. * 基于深度优先的有向图可达性算法
  3. * 求出给定顶点或一组顶点,有向图中能到达的点
  4. */
  5. public class DirectedDFS {
  6. private boolean[] marked; // 标记每个顶点是否可到达
  7. public DirectedDFS(DiGraph G, int s)
  8. {
  9. marked = new boolean[G.V()];
  10. dfs(G, s);
  11. }
  12. public DirectedDFS(DiGraph G, Iterable<Integer> sources)
  13. {
  14. marked = new boolean[G.V()];
  15. for (int v : sources) {
  16. if(!marked[v]){
  17. dfs(G, v);
  18. }
  19. }
  20. }
  21. private void dfs(DiGraph G, int v)
  22. {
  23. marked[v] = true;
  24. for (int w : G.adj(v)) {
  25. if(!marked[w])
  26. dfs(G, w);
  27. }
  28. }
  29. public boolean marked(int v) { return marked[v]; }
  30. public static void main(String[] args) {
  31. // 输入用例参加附录1
  32. DiGraph diGraph = new DiGraph(new Scanner(System.in));
  33. // 输出结果参加附录3
  34. // 测试顶点2到达的点
  35. System.out.println("顶点2到达的点");
  36. DirectedDFS reachable = new DirectedDFS(diGraph, 2);
  37. for (int i = 0; i < diGraph.V(); i++)
  38. if(reachable.marked(i)) System.out.print(i + " ");
  39. System.out.println();
  40. // 测试一组点:1,2,6能够到达的点
  41. System.out.println("1,2,6能够到达的点");
  42. DirectedDFS reachable2 = new DirectedDFS(diGraph, Arrays.asList(1, 2, 6));
  43. for (int i = 0; i < diGraph.V(); i++)
  44. if(reachable2.marked(i)) System.out.print(i + " ");
  45. System.out.println();
  46. }
  47. }

3 单点有向路径和单点最短有向路径

分别采用深度优先搜索和广度优先搜索实现

有向图的路径

  1. /**
  2. * 单点有向路径,给定顶点v,确定对于图中任一点w;
  3. * 是否存在v到w的路径,并输出路径;
  4. * 注意,深度优先搜索的路径无法保证是最短路径
  5. */
  6. public class DigraghDepthFirstPaths {
  7. // 标记点是否可达
  8. private boolean[] marked;
  9. // 记录到达点的那条边
  10. private int[] edge;
  11. private final int s;
  12. public DigraghDepthFirstPaths(DiGraph G, int s)
  13. {
  14. this.s = s;
  15. marked = new boolean[G.V()];
  16. edge = new int[G.V()];
  17. edge[s] = s;
  18. dfs(G, s);
  19. }
  20. private void dfs(DiGraph G, int v)
  21. {
  22. marked[v] = true;
  23. for (int w : G.adj(v)) {
  24. if(!marked[w]){
  25. edge[w] = v;
  26. dfs(G, w);
  27. }
  28. }
  29. }
  30. public boolean hasPathTo(int v){ return marked[v]; }
  31. public Stack<Integer> pathTo(int v)
  32. {
  33. Stack<Integer> paths = new Stack<>();
  34. for (int x=v; x!=s; x=edge[x]){
  35. paths.add(x);
  36. }
  37. paths.add(s);
  38. return paths;
  39. }
  40. public static void main(String[] args) {
  41. // 输入用例参加附录1
  42. DiGraph diGraph = new DiGraph(new Scanner(System.in));
  43. // 输出结果参加附录4
  44. // 构建顶点0到其他顶点的有向路径
  45. DigraghDepthFirstPaths depthFirstPaths = new DigraghDepthFirstPaths(diGraph, 0);
  46. System.out.print("顶点0可达的点: ");
  47. for (int i = 0; i < diGraph.V(); i++) {
  48. if (depthFirstPaths.hasPathTo(i)) System.out.print(i + " ");
  49. }
  50. System.out.println();
  51. // 是否存在有向路径
  52. if(depthFirstPaths.hasPathTo(12))
  53. System.out.println("0至12存在有向路径");
  54. else
  55. System.out.println("0至12不存在有向路径");
  56. // 顶点0到顶点3的一条有向路径
  57. System.out.print("0至3的一条有向路径: ");
  58. Stack<Integer> pathTo = depthFirstPaths.pathTo(3);
  59. while (!pathTo.isEmpty()){
  60. if (pathTo.size() == 1)
  61. System.out.print(pathTo.pop());
  62. else
  63. System.out.print(pathTo.pop() + " -> ");
  64. }
  65. System.out.println();
  66. }
  67. }

有向图的最短路径,基于广度优先算法

  1. /**
  2. * 基于广度优先搜索的单向路径算法;
  3. * 在此方法下,求得的路径为最短路径(忽略边权重)
  4. */
  5. public class DigraphBreadthFirstPaths {
  6. private boolean[] marked;
  7. // 采用队列保持带访问的顶点
  8. private ArrayDeque<Integer> enqueue;
  9. private int[] edge;
  10. private final int s;
  11. public DigraphBreadthFirstPaths(DiGraph G, int s)
  12. {
  13. this.s = s;
  14. marked = new boolean[G.V()];
  15. edge = new int[G.V()];
  16. enqueue = new ArrayDeque<>();
  17. enqueue.add(s);
  18. bfs(G);
  19. }
  20. private void bfs(DiGraph G)
  21. {
  22. while (!enqueue.isEmpty())
  23. {
  24. int v = enqueue.poll();
  25. for (int w : G.adj(v)) {
  26. if(!marked[w]){
  27. edge[w] = v;
  28. marked[w] = true;
  29. enqueue.add(w);
  30. }
  31. }
  32. }
  33. }
  34. public boolean hasPathTo(int v){ return marked[v]; }
  35. public Stack<Integer> pathTo(int v)
  36. {
  37. Stack<Integer> paths = new Stack<>();
  38. for (int x=v; x!=s; x=edge[x]){
  39. paths.add(x);
  40. }
  41. paths.add(s);
  42. return paths;
  43. }
  44. public static void main(String[] args) {
  45. // 输入用例参加附录1
  46. DiGraph diGraph = new DiGraph(new Scanner(System.in));
  47. // 输出结果参加附录5
  48. // 构建顶点0到其他顶点的有向路径
  49. DigraphBreadthFirstPaths breadthFirstPaths = new DigraphBreadthFirstPaths(diGraph, 0);
  50. System.out.print("顶点0可达的点: ");
  51. for (int i = 0; i < diGraph.V(); i++) {
  52. if (breadthFirstPaths.hasPathTo(i)) System.out.print(i + " ");
  53. }
  54. System.out.println();
  55. // 是否存在有向路径
  56. if(breadthFirstPaths.hasPathTo(12))
  57. System.out.println("0至12存在有向路径");
  58. else
  59. System.out.println("0至12不存在有向路径");
  60. // 顶点0到顶点3的最短路径
  61. System.out.print("0至3的一条有向路径: ");
  62. Stack<Integer> pathTo = breadthFirstPaths.pathTo(3);
  63. while (!pathTo.isEmpty()){
  64. if (pathTo.size() == 1)
  65. System.out.print(pathTo.pop());
  66. else
  67. System.out.print(pathTo.pop() + " -> ");
  68. }
  69. System.out.println();
  70. }
  71. }

4 检测有向图的环

检测有向图是否包含环,检测图没有环是拓扑排序的前提条件。

多数情况下,需要知道有向图是否包含环,并且输出够成环的边。

  1. /**
  2. * 基于深度优先搜索检测图中是否包含环
  3. */
  4. public class DirectedCycle {
  5. private boolean[] onStack;
  6. private Stack<Integer> cycle;
  7. private int[] edge;
  8. private boolean[] marked;
  9. public DirectedCycle(DiGraph G)
  10. {
  11. onStack = new boolean[G.V()];
  12. edge = new int[G.V()];
  13. marked = new boolean[G.V()];
  14. for (int i = 0; i < G.V(); i++) {
  15. if(!marked[i])
  16. dfs(G, i);
  17. }
  18. }
  19. private void dfs(DiGraph G, int v)
  20. {
  21. onStack[v] = true;
  22. marked[v] = true;
  23. for (int w : G.adj(v)) {
  24. if (this.hasCycle()) return;
  25. else if (!marked[w]){
  26. edge[w] = v; dfs(G, w); }
  27. // onStack[w]为true表明,当前v节点是一条经过w的抵达,表明w -> v有路径
  28. // 由于v -> w有边,因此必为环
  29. else if(onStack[w]){
  30. cycle = new Stack<>();
  31. for (int x = v; x != w; x=edge[x])
  32. cycle.push(x);
  33. cycle.push(w);
  34. cycle.push(v);
  35. }
  36. }
  37. onStack[v] = false;
  38. }
  39. public boolean hasCycle(){ return cycle != null; }
  40. public Iterable<Integer> cycle() { return cycle; }
  41. public static void main(String[] args) {
  42. // 输入用例参加附录1
  43. DiGraph diGraph = new DiGraph(new Scanner(System.in));
  44. // 输出结果参加附录6
  45. DirectedCycle directedCycle = new DirectedCycle(diGraph);
  46. System.out.println("有向图是否包含环: " + (directedCycle.hasCycle() ? "是" : "否"));
  47. if (directedCycle.hasCycle()){
  48. System.out.print("其中一条环为:");
  49. for (int i : directedCycle.cycle()) {
  50. System.out.print(i + " ");
  51. }
  52. }
  53. System.out.println();
  54. }
  55. }

5 顶点的深度优先次序

顶点的深度优先次序分为前序、后序和逆后续,区别是记录点的时机发生在递归调用的前还是后。该算法产生的pre、post和reversePost

顺序在图的高级算法中十分有用。

  1. public class DepthFirstOrder {
  2. private boolean[] marked;
  3. private ArrayDeque<Integer> pre; // 保存前序遍历的结果
  4. private ArrayDeque<Integer> post; // 保存后序的遍历结果
  5. private ArrayDeque<Integer> reversePost; //保存逆后序的遍历结果
  6. public DepthFirstOrder(DiGraph G)
  7. {
  8. marked = new boolean[G.V()];
  9. pre = new ArrayDeque<>();
  10. post = new ArrayDeque<>();
  11. reversePost = new ArrayDeque<>();
  12. for (int v=0; v<G.V(); v++)
  13. if (!marked[v]) dfs(G, v);
  14. }
  15. private void dfs(DiGraph G, int v)
  16. {
  17. marked[v] = true;
  18. pre.add(v);
  19. for (int w : G.adj(v))
  20. if(!marked[w])
  21. dfs(G, w);
  22. post.add(v);
  23. // 按post的倒序保存
  24. reversePost.addFirst(v);
  25. }
  26. public Iterable<Integer> pre(){ return pre; }
  27. public Iterable<Integer> post(){ return post; }
  28. public Iterable<Integer> reversePost(){ return reversePost; }
  29. public static void main(String[] args) {
  30. // 构造无环图的输入参见附录7
  31. DiGraph diGraph = new DiGraph(new Scanner(System.in));
  32. DepthFirstOrder depthFirstOrder = new DepthFirstOrder(diGraph);
  33. // 输出结果参加附录8
  34. // 注意:对于同一幅图,构造图的输入顺序不一致
  35. // 会导致输出不相同
  36. System.out.print("前序节点顺序: ");
  37. for (int v : depthFirstOrder.pre())
  38. System.out.print(v + " ");
  39. System.out.println();
  40. System.out.print("后续节点顺序:");
  41. for (int v : depthFirstOrder.post())
  42. System.out.print(v + " ");
  43. System.out.println();
  44. System.out.print("逆后序节点顺序:");
  45. for (int v : depthFirstOrder.reversePost())
  46. System.out.print(v + " ");
  47. }
  48. }

6 拓扑排序

给定一幅有向图,给出一组顶点排序,在有向图中,所有的边均是前面的点指向后面的点。

拓扑排序依赖图的环检测和逆后序遍历算法。

  1. /**
  2. * 计算有向无环图中的所有顶点的拓扑排序,
  3. * 通常用于解决优先级限制下的调度问题
  4. */
  5. public class Topological {
  6. private Iterable<Integer> order;
  7. public Topological(DiGraph G)
  8. {
  9. DirectedCycle directedCycle = new DirectedCycle(G);
  10. if(!directedCycle.hasCycle())
  11. order = new DepthFirstOrder(G).reversePost();
  12. }
  13. public boolean isDAG(){ return order == null; }
  14. public Iterable<Integer> order(){ return order; }
  15. public static void main(String[] args) {
  16. // 输入用例参考附录7
  17. DiGraph diGraph = new DiGraph(new Scanner(System.in));
  18. Topological topological = new Topological(diGraph);
  19. // 输出结果参见附录9
  20. if (topological.isDAG())
  21. System.out.println("有向图带有环,无法进行拓扑排序");
  22. else{
  23. System.out.print("拓扑排序结果:");
  24. for (int v : topological.order()) {
  25. System.out.print(v + " ");
  26. }
  27. }
  28. }
  29. }

7 强联通检测

如果存在从v至w的路径,同时还存在从w至v的路径,则称v和w之间是强连通;如果一幅有向图中任意两点间都

是强连通,则这幅有向图也是强连通的。检测强连通算法依赖图的反转和逆后序遍历算法。算法比较简洁,但是

理解起来比较难,需要仔细分析理解。

  1. /**
  2. * 有向图的强连通性,该算法依赖逆后序排序、图的反转、无向图的联通性算法
  3. */
  4. public class SCC {
  5. private int[] id;
  6. private int count;
  7. private boolean[] marked;
  8. public SCC(DiGraph G)
  9. {
  10. id = new int[G.V()];
  11. marked = new boolean[G.V()];
  12. DepthFirstOrder depthFirstOrder = new DepthFirstOrder(G.reverse());
  13. for (int v : depthFirstOrder.reversePost())
  14. if(!marked[v]) {
  15. dfs(G, v);
  16. count++;
  17. }
  18. }
  19. private void dfs(DiGraph G, int v)
  20. {
  21. id[v] = count;
  22. marked[v] = true;
  23. for (int w : G.adj(v))
  24. if(!marked[w])
  25. dfs(G, w);
  26. }
  27. // 两点是否是强连通
  28. public boolean stronglyConnected(int v, int w){ return id[v] == id[w]; }
  29. // 强联通分量数
  30. public int count(){ return count; }
  31. // 节点所在的联通分量标识符
  32. public int id(int v){ return id[v]; }
  33. public static void main(String[] args) {
  34. // 带环的图,输入用例参见附录1
  35. DiGraph diGraph = new DiGraph(new Scanner(System.in));
  36. // 输出结果参见附录10
  37. SCC scc = new SCC(diGraph);
  38. System.out.println("有向图中强连通分量数:" + scc.count());
  39. System.out.println("节点6与12是否是强连通:" + (scc.stronglyConnected(6, 12) ? "是" : "否"));
  40. System.out.println("节点9与12是否是强连通:" + (scc.stronglyConnected(9, 12) ? "是" : "否"));
  41. System.out.println("输出联通分量");
  42. for (int i = 0; i < scc.count(); i++) {
  43. for (int v = 0; v < diGraph.V(); v++) {
  44. if(scc.id[v] == i)
  45. System.out.print(v + " ");
  46. }
  47. System.out.println();
  48. }
  49. }
  50. }

附录1,有向图构造数据

  1. 13
  2. 22
  3. 4 2
  4. 2 3
  5. 3 2
  6. 6 0
  7. 0 1
  8. 2 0
  9. 11 12
  10. 12 9
  11. 9 10
  12. 9 11
  13. 8 9
  14. 10 12
  15. 11 4
  16. 4 3
  17. 3 5
  18. 7 8
  19. 8 7
  20. 5 4
  21. 0 5
  22. 6 4
  23. 6 9
  24. 7 6

附录2,有向图输出

  1. V: 13
  2. E: 22
  3. 0: 1 5
  4. 1:
  5. 2: 3 0
  6. 3: 2 5
  7. 4: 2 3
  8. 5: 4
  9. 6: 0 4 9
  10. 7: 8 6
  11. 8: 9 7
  12. 9: 10 11
  13. 10: 12
  14. 11: 12 4
  15. 12: 9

附录3:有向图的可达性测试

  1. 顶点2到达的点
  2. 0 1 2 3 4 5
  3. 126能够到达的点
  4. 0 1 2 3 4 5 6 9 10 11 12

附录4:基于深度优先搜索的单向路径测试结果

  1. 顶点0可达的点: 0 1 2 3 4 5
  2. 012不存在有向路径
  3. 03的一条有向路径: 0 -> 5 -> 4 -> 2 -> 3

附录5:基于广度优先搜索的最短路径测试结果

  1. 顶点0可达的点: 0 1 2 3 4 5
  2. 012不存在有向路径
  3. 03的一条有向路径: 0 -> 5 -> 4 -> 3

附录6:检测环算法的测试输出

  1. 有向图是否包含环:
  2. 其中一条环为:3 2 4 5 3

附录7:构造无环图的输入用例

  1. 13
  2. 15
  3. 0 1
  4. 0 5
  5. 0 6
  6. 2 0
  7. 2 3
  8. 3 5
  9. 5 4
  10. 6 4
  11. 6 9
  12. 7 6
  13. 8 7
  14. 9 10
  15. 9 11
  16. 9 12
  17. 11 12

附录8:深度优先遍历图的输出结果

  1. 前序节点顺序: 0 1 5 4 6 9 10 11 12 2 3 7 8
  2. 后续节点顺序:1 4 5 10 12 11 9 6 0 3 2 7 8
  3. 逆后序节点顺序:8 7 2 3 0 6 9 11 12 10 5 4 1

附录9:拓扑排序测试输出结果

  1. 拓扑排序结果:8 7 2 3 0 6 9 11 12 10 5 4 1

附录10:带环有向图的强连通性测试输出结果

  1. 有向图中强连通分量数:5
  2. 节点612是否是强连通:否
  3. 节点912是否是强连通:是
  4. 输出联通分量
  5. 1
  6. 0 2 3 4 5
  7. 9 10 11 12
  8. 6
  9. 7 8

有向图的基本算法-Java实现的更多相关文章

  1. 无向图的最短路径算法JAVA实现

    一,问题描述 给出一个无向图,指定无向图中某个顶点作为源点.求出图中所有顶点到源点的最短路径. 无向图的最短路径其实是源点到该顶点的最少边的数目. 本文假设图的信息保存在文件中,通过读取文件来构造图. ...

  2. 无向图的最短路径算法JAVA实现(转)

    一,问题描述 给出一个无向图,指定无向图中某个顶点作为源点.求出图中所有顶点到源点的最短路径. 无向图的最短路径其实是源点到该顶点的最少边的数目. 本文假设图的信息保存在文件中,通过读取文件来构造图. ...

  3. 归并排序算法 java 实现

    归并排序算法 java 实现 可视化对比十多种排序算法(C#版) [直观学习排序算法] 视觉直观感受若干常用排序算法 算法概念 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Di ...

  4. 快速排序算法 java 实现

    快速排序算法 java 实现 快速排序算法Java实现 白话经典算法系列之六 快速排序 快速搞定 各种排序算法的分析及java实现 算法概念 快速排序是C.R.A.Hoare于1962年提出的一种划分 ...

  5. 堆排序算法 java 实现

    堆排序算法 java 实现 白话经典算法系列之七 堆与堆排序 Java排序算法(三):堆排序 算法概念 堆排序(HeapSort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,可以利用数组的特 ...

  6. Atitit 电子商务订单号码算法(java c# php js 微信

    Atitit 电子商务订单号码算法(java c# php js  微信 1.1. Js版本的居然钱三爷里面没有..只好自己实现了. 1.2. 订单号标准化...长度16位 1.3. 订单号的结构 前 ...

  7. 基于FP-Tree的关联规则FP-Growth推荐算法Java实现

    基于FP-Tree的关联规则FP-Growth推荐算法Java实现 package edu.test.ch8; import java.util.ArrayList; import java.util ...

  8. 双色球机选算法java实现

    双色球机选算法java实现 一.代码 package com.hdwang; import java.util.Random; /** * Created by admin on 2017/1/10. ...

  9. Floyd算法java实现demo

    Floyd算法java实现,如下: https://www.cnblogs.com/Halburt/p/10756572.html package a; /** * ┏┓ ┏┓+ + * ┏┛┻━━━ ...

随机推荐

  1. 7.hbase shell命令 cmd

    $HADOOP_USER_NAME #创建命名空间create_namespace 'bd1902' #展示所有命名空间 list_namespace #删除命名空间,The namespace mu ...

  2. HDFS的数据流读写数据 (面试开发重点)

    1 HDFS写数据流程 1.1 剖析文件写入 HDFS写数据流程,如图所示 1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是 ...

  3. SwitchyOmega 配置

    1.google 扩展程序里面的chrome 网上应用店里面安装Proxy SwitchyOmega 2.新建情景模式 3.配置代理 4.自动切换添加新建的情景模式,最后保存

  4. Overcoming Forgetting in Federated Learning on Non-IID Data

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 以下是对本文关键部分的摘抄翻译,详情请参见原文. NeurIPS 2019 Workshop on Federated Learning ...

  5. wordpress个人常用标签调用

    wordpress常见标签调用,老是容易忘记,又要找半天,干脆搬到网站上. <?php bloginfo('name');?>网站名称 url <?php echo home_url ...

  6. 推荐一款万能抓包神器:Fiddler Everywhere

    搞IT技术的同行,相信没有几个人是不会抓包这项技能的(如果很不幸你中枪了,那希望这篇文章给你一些动力),市面上的抓包工具也有很多,常用的有:Charles.Fiddler.Burpsuite.Wire ...

  7. oracle再回首

    第一章 Oracle 数据库的使用   一. 数据库相关概念   1 什么是数据库 所谓的数据库其实就是数据的集合.用户可以对集合中的数据进行新增.查询.更新. 删除等操作.数据库是以一定方式储存在一 ...

  8. 用rspec执行自动化测试用例

    rspec是一款行为驱动开发(BDD)的工具,不过在这里用于测试,准确来说应该是测试驱动开发(TDD)吧.事实上我也没搞清楚.作为初学者不清楚就不清楚吧,以后会知道的.写博客无非就是写写学习笔记,不纠 ...

  9. Lua C API的正确用法

    http://blog.codingnow.com/2015/05/lua_c_api.html http://blog.csdn.net/oilcode/article/details/510861 ...

  10. nginx server_name 多个

    nginx server_name 多个 nginx server_name 多个的话,空格隔开就行 server_name baidu.com baidu.me; 如果很多的话可以用正则,我的需求, ...