数据结构 -- 无向图

一、概述

图是由一组顶点和一组能够将两个顶点相连的边组成的

二、邻接矩阵实现(todo)

2.1 API设计

2.2 实现

2.3 测试

三、邻接表实现

存储结构:邻接矩阵、邻接表

3.1 API设计

类名 Graph
构造方法 Graph(int V):创建一个包含V个顶点但不包含边的图
成员方法 1.public int V():获取图中顶点的数量
2.public int E():获取图中边的数量
3.public void addEdge(int v,int w):向图中添加一条边 v-w
4.public Queue adj(int v):获取和顶点v相邻的所有顶点
成员变量 1.private final int V: 记录顶点数量
2.private int E: 记录边数量
3.private Queue[] adj: 邻接表

3.2 实现

public class Graph {
    
    
    // 顶点数目
    private final int V;
    // 边的数目
    private int E;
    // 邻接表
    private Queue<Integer>[] adj;

    public Graph(int v) {
    
    
        // 初始化顶点数目
        V = v;
        // 初始化边的数量
        this.E = 0;
        // 初始化邻接表
        this.adj = new Queue[V];
        // 初始化邻接表中的空队列
        for (int i = 0; i < adj.length; i++) {
    
    
            adj[i] = new Queue<Integer>();
        }
    }

    // 获取图中顶点的数量
    public int V(){
    
    
        return V;
    }

    // 获取图中边的数量
    public int E(){
    
    
        return E;
    }

    // 向图中添加一条边v-w
    public void addEdge(int v, int w){
    
    
        // 把w添加到v的链表中,这样顶点v就多了一个相邻点w
        adj[v].enqueue(w);

        // 把v添加到w的链表中,这样顶点w就多了一个相邻点v
        adj[w].enqueue(v);

        // 边的数目自增1
        E++;
    }

    // 获取和顶点v相邻的所有顶点
    public Queue<Integer> adj(int v){
    
    
        return adj[v];
    }
}

3.3 测试

public class GraphTest {
    
    
    public static void main(String[] args) {
    
    
        Graph graph = new Graph(10);
        graph.addEdge(1,2);
        graph.addEdge(1,3);
        System.out.println("图的边的条数: " + graph.E());
    }
}

四、图的搜索

4.1 深度优先搜索

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。

4.1.1 API设计

类名 DepthFirstSearch
构造方法 DepthFirstSearch(Graph G,int s):构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相通顶点
成员方法 1.private void dfs(Graph G, int v):使用深度优先搜索找出G图中v顶点的所有相通顶点
2.public boolean marked(int w):判断w顶点与s顶点是否相通
3.public int count():获取与顶点s相通的所有顶点的总数
成员变量 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索
2.private int count:记录有多少个顶点与s顶点相通

4.1.2 实现

/**
 * @date 2021/7/8 11:32
 */
public class DepthFirstSearch {
    
    
    // 索引代表顶点,值代表当前顶点是否已经被搜索
    private boolean[] marked;

    // 记录有多少个顶点与s顶点相同
    private int count;

    // 构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相邻顶点
    public DepthFirstSearch(Graph G, int s) {
    
    
        // 初始化marked数组
        this.marked = new boolean[G.V()];

        // 初始化跟顶点s相通的顶点的数量
        this.count = 0;

        dfs(G,s);
    }

    // 使用深度优先搜索找出G图中v顶点的所有相邻顶点
    private void dfs(Graph G, int v){
    
    
        // 把当前顶点标记为已搜索
        marked[v] = true;

        for (Integer w : G.adj(v)) {
    
    
            // 判断当前w顶点有没有被搜索过,如果没有被搜索过,则递归调用dfs方法进行深度搜索
            if (!marked[w]){
    
    
                dfs(G,w);
            }
        }

        // 相通顶点数量+1
        count++;
    }

    // 判断w顶点与s顶点是否相通
    public boolean marked(int w){
    
    
        return marked[w];
    }

    // 获取与顶点s相通的所有顶点的总数
    public int count(){
    
    
        return count;
    }
}

4.1.3 测试

    // 测试深度优先遍历
    public static void test02(){
    
    
        Graph graph = new Graph(10);
        graph.addEdge(1,2);
        graph.addEdge(1,3);

        DepthFirstSearch depthFirstSearch = new DepthFirstSearch(graph,1);
        System.out.println("与顶点1相通的所有顶点的总数:" + depthFirstSearch.count());

        System.out.println("顶点2与顶点1是否相通:" + depthFirstSearch.marked(2));
        System.out.println("顶点4与顶点1是否相通:" + depthFirstSearch.marked(4));
    }

4.5 广度优先搜索

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找兄弟结点,然后找子结点。

4.5.1 API设计

类名 BreadthFirstSearch
构造方法 BreadthFirstSearch(Graph G,int s):构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻顶点
成员方法 1.private void bfs(Graph G, int v):使用广度优先搜索找出G图中v顶点的所有相邻顶点
2.public boolean marked(int w):判断w顶点与s顶点是否相通
3.public int count():获取与顶点s相通的所有顶点的总数
成员变量 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索
2.private int count:记录有多少个顶点与s顶点相通
3.private Queue waitSearch: 用来存储待搜索邻接表的点

4.5.2 实现

/**
 * @date 2021/7/8 14:37
 */
public class BreadthFirstSearch {
    
    
    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;
    //记录有多少个顶点与s顶点相通
    private int count;
    //用来存储待搜索邻接表的点
    private Queue<Integer> waitSearch;

    // 构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻节点
    public BreadthFirstSearch(Graph G, int s) {
    
    
        this.marked = new boolean[G.V()];
        this.count = 0;
        this.waitSearch = new Queue<Integer>();

        bfs(G,s);
    }

    // 使用广度优先搜索找出G图中s顶点的所有相邻节点
    private void bfs(Graph G, int v){
    
    
        // 把当前顶点v标识为已搜索
        marked[v] = true;
        // 让顶点v进入队列,待搜索
        waitSearch.enqueue(v);
        // 通过循环,如果队列不为空,则从队列中弹出一个带搜索的顶点进行搜索
        while (!waitSearch.isEmpty()){
    
    
            // 弹出一个待搜索的顶点
            Integer wait = waitSearch.dequeue();

            // 遍历wait顶点的邻接表
            for (Integer w : G.adj(wait)) {
    
    
                if (!marked[w]){
    
    
                    bfs(G, w);
                }
            }
        }

        // 让相通的节点加一
        count++;
        
    }

    // 判断w顶点与s顶点是否相通
    public boolean marked(int w){
    
    
        return marked[w];
    }

    // 获取与顶点s相通的所有顶点的总数
    public int count(){
    
    
        return count;
    }
}

4.5.3 测试

    // 测试广度优先遍历
    public static void test03(){
    
    
        Graph graph = new Graph(10);
        graph.addEdge(1,2);
        graph.addEdge(1,3);

        BreadthFirstSearch breadthFirstSearch = new BreadthFirstSearch(graph,1);
        System.out.println("与顶点1相通的所有顶点的总数:" + breadthFirstSearch.count());

        System.out.println("顶点2与顶点1是否相通:" + breadthFirstSearch.marked(2));
        System.out.println("顶点4与顶点1是否相通:" + breadthFirstSearch.marked(4));
    }

五、路径查找

实现:基于深度优先搜索、基于广度优先搜索
本文采用基于深度优先遍历来查找路径

5.1 API设计

类名 DepthFirstPaths
构造方法 DepthFirstPaths(Graph G,int s):构造深度优先搜索对象,使用深度优先搜索找出G图中起点为s的所有路径
成员方法 1.private void dfs(Graph G, int v):使用深度优先搜索找出G图中v顶点的所有相邻顶点
2.public boolean hasPathTo(int v):判断v顶点与s顶点是否存在路径
3.public Stack pathTo(int v):找出从起点s到顶点v的路径(就是该路径经过的顶点)
成员变量 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索
2.private int s:起点
3.private int[] edgeTo:索引代表顶点,值代表从起点s到当前顶点路径上的最后一个顶点

5.2 实现

/**
 * @date 2021/7/8 15:38
 */
public class DepthFirstPaths {
    
    
    // 索引代表顶点,值代表当前顶点是否已经被搜索
    private boolean[] marked;
    // 起点
    private int s;
    // 路径 索引代表顶点,值代表从起点s到当前顶点路径上的最后一个顶点
    private int[] edgeTo;

    // 构造深度优先搜索对象,使用深度优先搜索找出G图中起点为s的所有路径
    public DepthFirstPaths(Graph G, int s) {
    
    
        // 初始化marked数组
        this.marked = new boolean[G.V()];
        // 初始化起点
        this.s = s;
        // 初始化edgeTo数组
        this.edgeTo = new int[G.V()];

        dfs(G, s);
    }

    // 使用深度优先搜索找出G图中v顶点的所有相邻顶点
    private void dfs(Graph G, int v){
    
    
        // 把v标识为已搜索
        marked[v] = true;

        // 遍历顶点v的邻接表,拿到每一个相邻的顶点,继续递归搜索
        for (Integer w : G.adj(v)) {
    
    
            // 如果顶点w没有被搜索,则继续递归搜索
            if (!marked[w]){
    
    
                edgeTo[w] = v; // 到达顶点w的路径上的最后一个顶点是v
                dfs(G, w);
            }
        }
    }

    // 判断w顶点与s顶点是否存在路径
    public boolean hasPathTo(int v){
    
    
        return marked[v];
    }

    // 找出从起点s到顶点v的路径(就是该路径经过的顶点)
    public Stack<Integer> pathTo(int v){
    
    
        if (!hasPathTo(v)){
    
    
            return null;
        }

        // 创建栈对象,保存路径中的所有顶点
        Stack<Integer> path = new Stack<>();

        // 通过循环,从顶点v开始,一直往前找,直到找到起点为止
        for (int x = v; x != s ; x = edgeTo[x]) {
    
    
            path.push(x);
        }

        // 把起点s放到栈中
        path.push(s);

        return path;
    }
}

5.3 测试

    // 测试基于深度优先查找路径
    public static void test04(){
    
    
        Graph graph = new Graph(10);
        graph.addEdge(1,2);
        graph.addEdge(2,3);
        graph.addEdge(3,4);
        graph.addEdge(2,4);

        DepthFirstPaths depthFirstPaths = new DepthFirstPaths(graph,1);

        System.out.println("判断顶点2到顶点1有没有路径:" + depthFirstPaths.hasPathTo(2));
        System.out.println("判断顶点4到顶点1有没有路径:" + depthFirstPaths.hasPathTo(6));

        Stack<Integer> pathTo4 = depthFirstPaths.pathTo(4);
        StringBuilder sb = new StringBuilder();
        for (Integer v : pathTo4) {
    
    
            sb.append(v+"-");
        }
        sb.deleteCharAt(sb.length()-1);
        System.out.println("顶点4到顶点1的路径:" + sb);
    }

猜你喜欢

转载自blog.csdn.net/m0_46218511/article/details/118566479
今日推荐