zoukankan      html  css  js  c++  java
  • 图的基本表示-邻接矩阵和邻接表

    简介

    一个图主要包括顶点和边两部分。

    • 自环边
      自己到自己形成的边,如图中的0顶点
    • 平行边
      顶点3和顶点4这种情况就是平行边
    • 简单图
      没有自环边和平行边的图
    • 连通图
      图中任意两个顶点之间都有路径
    • 连通分量
      图中包含的连通子图的个数,如上图有2个连通分量
    • 有环图
      图中顶点之间可以形成环,上图0,1,2,3顶点之间存在环。

    图根据边是否有方向和边是否有权重可以分为

    1. 无向无权图,如好友关系
    2. 有向无权图,如关注关系
    3. 无向有权图,如城市之间的关系,城市之间的距离就是权重
    4. 有向有权图,如一个城市的地铁线路

    图的表示方式

    以无向无权图且简单图为例。图有两种表示方式,邻接矩阵和邻接表。

    邻接矩阵

    使用邻接矩阵表示为

    0 1 2 3 4 5 6
    0 0 1 0 1 0 0 0
    1 1 0 1 0 0 0 1
    2 0 1 0 1 0 1 0
    3 1 0 1 0 1 0 0
    4 0 0 0 1 0 1 0
    5 0 0 1 0 1 0 1
    6 0 1 0 0 0 1 0
    A[i][j]=1表示顶点i和订单j相邻
    代码实现

    定义一个接口表示图的各种操作

    public interface Graph {
    
      /**
       * 查询总顶点数
       */
      int V();
    
      /**
       * 查询总边数
       */
      int E();
    
      /**
       * 两个顶点之间是否有边
       */
      boolean hasEdge(int v, int w);
    
      /**
       * 查询一个顶点的所有连接顶点
       */
      Iterable<Integer> adj(int v);
    
      /**
       * 查询一个顶点的度(连接顶点的个数)
       */
      int degree(int v);
    
      /**
       * 检查v顶点是否合法
       */
      void validateVertex(int v);
    
    }
    

    邻接矩阵实现

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    import java.util.Scanner;
    
    public class AdjMatrix implements Graph {
    
      /**
       * 总顶点数
       */
      private int V;
      /**
       * 总边数
       */
      private int E;
      /**
       * 顶点之间的关系
       */
      private int[][] adj;
    
      public AdjMatrix(String fileName) {
        try (InputStream is = AdjMatrix.class.getResourceAsStream(fileName);
            Scanner scanner = new Scanner(is)) {
          V = scanner.nextInt();
          if (V < 0) {
            throw new IllegalArgumentException("V must be non-negative");
          }
          adj = new int[V][V];
          E = scanner.nextInt();
          if (E < 0) {
            throw new IllegalArgumentException("E must be non-negative");
          }
          for (int i = 0; i < E; i++) {
            int a = scanner.nextInt();
            validateVertex(a);
            int b = scanner.nextInt();
            validateVertex(b);
            if (a == b) {
              throw new IllegalArgumentException("Self Loop is Detected!");
            }
            if (adj[a][b] == 1) {
              throw new IllegalArgumentException("Parallel Edges are Detected!");
            }
            adj[a][b] = 1;
            adj[b][a] = 1;
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    
      public void validateVertex(int v) {
        if (v < 0 || v >= V) {
          throw new IllegalArgumentException("vertex " + v + "is invalid");
        }
      }
    
      @Override
      public int V() {
        return V;
      }
    
      @Override
      public int E() {
        return E;
      }
    
      @Override
      public boolean hasEdge(int v, int w) {
        validateVertex(v);
        validateVertex(w);
        return adj[v][w] == 1;
      }
    
      @Override
      public Collection<Integer> adj(int v) {
        validateVertex(v);
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < V; i++) {
          if (adj[v][i] == 1) {
            res.add(i);
          }
        }
        return res;
      }
    
      @Override
      public int degree(int v) {
        return adj(v).size();
      }
    
      @Override
      public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("V = %d, E = %d
    ", V, E));
        for (int i = 0; i < V; i++) {
          for (int j = 0; j < V; j++) {
            sb.append(String.format("%d ", adj[i][j]));
          }
          sb.append('
    ');
        }
        return sb.toString();
      }
    
      public static void main(String[] args) {
        Graph graph = new AdjMatrix("g.txt");
        System.out.println(graph);
        System.out.println(graph.degree(0));
        System.out.println(graph.adj(0));
      }
    }
    

    g.txt为数据文件,内容如下

    7 9
    0 1
    0 3
    1 2
    1 6
    3 2
    3 4
    5 6
    5 2
    5 4
    

    第一行表示顶点个数和边个数,后面行表示顶点和顶点之间的连接关系。
    邻接矩阵空间复杂度为O(V^2),V表示顶点数,求相邻顶点时间复杂度为O(V),求两个顶点之间是否相邻时间复杂度为O(1)。对于稀疏图(边个数远小于顶点数的平方),邻接矩阵这种表示方式会造成大量的空间浪费,时间复杂度也比较高,我们可以使用邻接表来优化。

    邻接表

    0 : 1 3 
    1 : 0 2 6 
    2 : 1 3 5 
    3 : 0 2 4 
    4 : 3 5 
    5 : 6 2 4 
    6 : 1 5
    

    每个顶点关联一个链表,表示邻接顶点

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.LinkedList;
    import java.util.Scanner;
    
    public class AdjList implements Graph {
    
      private int V;
      private int E;
      private LinkedList<Integer>[] adj;
    
      public AdjList(String fileName) {
        try (InputStream is = AdjList.class.getResourceAsStream(fileName);
            Scanner scanner = new Scanner(is)) {
          V = scanner.nextInt();
          if (V < 0) {
            throw new IllegalArgumentException("V must be non-negative");
          }
          adj = new LinkedList[V];
          for (int i = 0; i < V; i++) {
            adj[i] = new LinkedList<>();
          }
          E = scanner.nextInt();
          if (E < 0) {
            throw new IllegalArgumentException("E must be non-negative");
          }
          for (int i = 0; i < E; i++) {
            int a = scanner.nextInt();
            validateVertex(a);
            int b = scanner.nextInt();
            validateVertex(b);
            if (a == b) {
              throw new IllegalArgumentException("Self Loop is Detected!");
            }
            if (adj[a].contains(b)) {
              throw new IllegalArgumentException("Parallel Edges are Detected!");
            }
            adj[a].add(b);
            adj[b].add(a);
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    
      public void validateVertex(int v) {
        if (v < 0 || v >= V) {
          throw new IllegalArgumentException("vertex " + v + "is invalid");
        }
      }
    
      @Override
      public int V() {
        return V;
      }
    
      @Override
      public int E() {
        return E;
      }
    
      @Override
      public boolean hasEdge(int v, int w) {
        validateVertex(v);
        validateVertex(w);
        return adj[v].contains(w);
      }
    
      @Override
      public Iterable<Integer> adj(int v) {
        validateVertex(v);
        return adj[v];
      }
    
      @Override
      public int degree(int v) {
        validateVertex(v);
        return adj[v].size();
      }
    
      @Override
      public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("V = %d, E = %d
    ", V, E));
        for (int v = 0; v < V; v++) {
          sb.append(String.format("%d : ", v));
          for (int w : adj[v]) {
            sb.append(String.format("%d ", w));
          }
          sb.append('
    ');
        }
        return sb.toString();
      }
    
      public static void main(String[] args) {
    
        AdjList adjList = new AdjList("g.txt");
        System.out.print(adjList);
      }
    }
    

    这里使用LinkedList来保存顶点的邻接顶点,空间复杂度为O(V+E),V表示顶点数,E表示边数,求两个顶点是否相邻和求顶点的邻接顶点的时间复杂度都为O(degree(v)),degree(v)表示一个顶点的度,一般远远小于顶点数V。那么有没有更好的性能呢,我们可以使用TreeSet代替LinkedList,将时间复杂度降为O(logV)。

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Scanner;
    import java.util.TreeSet;
    
    public class AdjSet implements Graph {
    
      private int V;
      private int E;
      private TreeSet<Integer>[] adj;
    
      public AdjSet(String fileName) {
    
        try (InputStream is = AdjSet.class.getResourceAsStream(fileName);
            Scanner scanner = new Scanner(is)) {
    
          V = scanner.nextInt();
          if (V < 0) {
            throw new IllegalArgumentException("V must be non-negative");
          }
          adj = new TreeSet[V];
          for (int i = 0; i < V; i++) {
            adj[i] = new TreeSet<Integer>();
          }
    
          E = scanner.nextInt();
          if (E < 0) {
            throw new IllegalArgumentException("E must be non-negative");
          }
    
          for (int i = 0; i < E; i++) {
            int a = scanner.nextInt();
            validateVertex(a);
            int b = scanner.nextInt();
            validateVertex(b);
    
            if (a == b) {
              throw new IllegalArgumentException("Self Loop is Detected!");
            }
            if (adj[a].contains(b)) {
              throw new IllegalArgumentException("Parallel Edges are Detected!");
            }
    
            adj[a].add(b);
            adj[b].add(a);
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    
      public void validateVertex(int v) {
        if (v < 0 || v >= V) {
          throw new IllegalArgumentException("vertex " + v + "is invalid");
        }
      }
    
      @Override
      public int V() {
        return V;
      }
    
      @Override
      public int E() {
        return E;
      }
    
      @Override
      public boolean hasEdge(int v, int w) {
        validateVertex(v);
        validateVertex(w);
        return adj[v].contains(w);
      }
    
      @Override
      public Iterable<Integer> adj(int v) {
        validateVertex(v);
        return adj[v];
      }
    
      @Override
      public int degree(int v) {
        validateVertex(v);
        return adj[v].size();
      }
    
      @Override
      public String toString() {
        StringBuilder sb = new StringBuilder();
    
        sb.append(String.format("V = %d, E = %d
    ", V, E));
        for (int v = 0; v < V; v++) {
          sb.append(String.format("%d : ", v));
          for (int w : adj[v]) {
            sb.append(String.format("%d ", w));
          }
          sb.append('
    ');
        }
        return sb.toString();
      }
    
      public static void main(String[] args) {
        AdjSet adjSet = new AdjSet("g.txt");
        System.out.print(adjSet);
      }
    }
    

    方式比较

    空间 建图时间 查看两点是否相邻 查找点的所有相邻顶点
    邻接矩阵 O(V^2) O(E) O(1) O(V)
    邻接表(LinkedList) O(V+E) O(E*V) O(degree(v)) O(degree(v))
    邻接表(TreeSet) O(V+E) O(ElogV) O(logV) O(degree(v))
    综合比较,邻接表(TreeSet)性能更好。
  • 相关阅读:
    vue-element-admin中table分页改为前台处理
    vue项目如何部署到Tomcat中
    vuex之modules 热加载(hot update)
    持续学习
    css比较特殊选择器汇总(持续更新)
    关于伪元素before after总结
    ajax入门-实现省份下拉框
    super和this关键字的详解
    监听器
    当浏览器被关闭时,session是否被关闭?
  • 原文地址:https://www.cnblogs.com/strongmore/p/14546716.html
Copyright © 2011-2022 走看看