数据结构 -- 无向图

一、概述

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

二、邻接矩阵实现(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);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值