0
点赞
收藏
分享

微信扫一扫

数据结构与算法 -- 图

mm_tang 2022-04-02 阅读 96

目录

图的概念 

图的存储

        邻接矩阵

        邻接表

图的遍历

       深度优先搜索 (DFS)

       广度优先搜索 (BFS)


图的概念 

图一

         图(Graph)是一种复杂的非线性表结构。 图中的元素叫做顶点(vertex),图中的一个顶点可以与任意其他顶点建立连接关系。把这种建立的关系叫做边(edge),跟顶点相连接的边的条数叫做度(degree)。

        边有方向的图叫做有向图,比如A点到B点的直线距离,微信的添加好友是双向的。

        边无方向的图叫无向图,比如网络拓扑图。

        带权图(weighted graph)则是在图中,每条边都有一个权重(weight),我们可以通过这个权重来表示 一些可度量的值。

        图具有很广泛的应用,比如社交网络,电子地图,多对多的关系就可以用图来表示。

图的存储

        邻接矩阵

        图最直观的存储方法就是,邻接矩阵,邻接矩阵的底层是一个二维数组,如图一所示,采用临界矩阵的方式如下

        当图为无向图时,顶点 A 与顶点 B 有边相连,则在邻接矩阵中 [A][B] 与 [B][A] 都用 1 来表示

        当图为有向图时,若顶点 A 指向顶点 B,则在邻接矩阵中 [A][B] 用 1 来表示

        当图为带权图时,则在邻接矩阵中用 对应边的权重 来表示

import java.util.ArrayList;
import java.util.List;

/**
 * 邻接矩阵实现
 */
public class Graph1 {

    /** 存储点的链表 */
    private List vertexList;

    /**邻接矩阵,用来存储边 */
    private int[][] edges;

    /** 边的数目 */
    private int numOfEdges;

    public Graph1(int n) {
        //初始化邻接矩阵,一维数组,和边的数目
        edges = new int[n][n];
        vertexList = new ArrayList(n);
        numOfEdges = 0;
    }

    /** 得到结点的个数 */
    public int getNumOfVertex(){
        return vertexList.size();
    }

    /** 得到边的数目 */
    public int getNumOfEdges(){
        return numOfEdges;
    }

    /** 返回结点 i 的数据 */
    public Object getValueByIndex(int n){
        return vertexList.get(n);
    }

    /** 返回v1, v2的权值 */
    public int getWeight(int v1, int v2){
        return edges[v1][v2];
    }

    /** 插入结点 */
    public void insertVertex(Object vertex){
        vertexList.add(vertex);
    }

    /** 插入边 */
    public void insertEdge(int v1, int v2, int weight){
        edges[v1][v2] = weight;
        numOfEdges++;
    }

}

class testGraph{
    public static void main(String[] args) {
        //结点个数与边的个数
        int n = 4, e = 4;
        //结点标识
        String labels[] = {"v1", "v2", "v3", "v4"};

        Graph1 graph1 = new Graph1(n);

        //插入节点
        for (String label : labels) {
            graph1.insertVertex(label);
        }

        graph1.insertEdge(0, 1, 2);
        graph1.insertEdge(0, 2, 5);
        graph1.insertEdge(2, 3, 2);
        graph1.insertEdge(3, 1, 9);

        System.out.println("结点个数:" + graph1.getNumOfVertex());
        System.out.println("边个数:" + graph1.getNumOfEdges());

    }
}

        邻接表

        用邻接矩阵来表示一个图,简单直观但非常浪费空间,对于无向图来说,如果A[i][j] = 1 则 A[j][i] = 1。因此我们只需要存储一个就可以了。也就是说,无向图的二维数组中,如果我们将其用对角线划分上下两部分,只需要利用上面或下面一半的空间就足够了,另一半空间完全浪费掉了,如果我们存储的是稀疏图,顶点多,但是边较少,采用邻接矩阵的方法更加的浪费空间。

        因此针对邻接矩阵浪费空间的问题,我们通过邻接表的方法来解决。

        每一个顶点对应一个链表,链表中存储与这个顶点相连接的其他顶点。

        如图我们如果采用邻接表的方法来存储的话就是

         根据邻接表的结构和图,不难发现,图其实就是由顶点和边来组成的,因此我们可以抽象出来两个类,一个是Vertext类,一个是Edge边类

/**
 * 顶点
 */
class Vertex {
    String name;
    Edge next;

    public Vertex(String name, Edge next) {
        this.name = name;
        this.next = next;
    }
}

/**
 * 边
 */
class Edge {
    String name;
    int weight;
    Edge next;

    public Edge(String name, int weight, Edge next) {
        this.name = name;
        this.weight = weight;
        this.next = next;
    }
}

public class Graph2 {

    Map<String, Vertex> vertexMap;
    Graph2(){
        this.vertexMap = new HashMap<>();
    }

    public void insertVertex(String vertexName){
        Vertex vertex = new Vertex(vertexName, null);
        vertexMap.put(vertexName, vertex);
    }

    public void insertEdge(String begin, String end, int weight){

        Vertex beginVertex = vertexMap.get(begin);
        if (beginVertex == null){
            insertVertex(begin);
        }

        Edge edge = new Edge(end, weight, null);

        if(beginVertex.next == null){
            beginVertex.next = edge;
        } else {
            Edge lastEdge = beginVertex.next;
            while(lastEdge.next != null){
                lastEdge = lastEdge.next;
            }
            lastEdge.next = edge;
        }

    }

    public void print(){

        Set<Map.Entry<String, Vertex>> set = vertexMap.entrySet();
        Iterator<Map.Entry<String, Vertex>> iterator = set.iterator();
        while (iterator.hasNext()){
            Map.Entry<String, Vertex> entry = iterator.next();
            Vertex vertex = entry.getValue();
            Edge edge = vertex.next;
            while (edge != null){
                System.out.println(vertex.name + "指向" + edge.name + "权值为:" + edge.weight);
                edge = edge.next;
            }
        }
    }

}

class testGraph2{
    public static void main(String[] args) {
        Graph2 graph = new Graph2();
        graph.insertVertex("A");
        graph.insertVertex("B");
        graph.insertVertex("C");
        graph.insertVertex("D");
        graph.insertVertex("E");
        graph.insertVertex("F");

        graph.insertEdge("C", "A", 1);
        graph.insertEdge("F", "C", 2);
        graph.insertEdge("A", "B", 4);
        graph.insertEdge("E", "B", 2);
        graph.insertEdge("A", "D", 5);
        graph.insertEdge("D", "F", 4);
        graph.insertEdge("D", "E", 3);
        graph.print();

    }
}

图的遍历

        图的遍历是指,从给定图中任意指定的顶点出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每一个顶点仅被访问一次,这个过程是 图的遍历。 遍历过程中得到的顶点序列称为图遍历序列

       深度优先搜索 (DFS)

       广度优先搜索 (BFS)

举报

相关推荐

0 条评论