无向图-java语言版

425 阅读5分钟

在现实生活中,有许多应用场景会包含很多点以及点点之间的连接,而这些应用场景我们都可以用即将要学习的图 这种数据结构去解决。

图.jpg

图的分类: 
按照连接两个顶点的边的不同,可以把图分为以下两种: 
    1、无向图:边仅仅连接两个顶点,没有其他含义; 
    2、有向图:边不仅连接两个顶点,并且具有方向;

图的相关术语
    1、邻顶点:当两个顶点通过一条边相连时,我们称这两个顶点是相邻的,并且称这条边依附于这两个顶点。 
    2、度:某个顶点的度就是依附于该顶点的边的个数 
    3、子图:是一幅图的所有边的子集(包含这些边依附的顶点)组成的图; 
    4、路径:是由边顺序连接的一系列的顶点组成 
    5、环: 是一条至少含有一条边且终点和起点相同的路径
    6、连通图: 如果图中任意一个顶点都存在一条路径到达另外一个顶点,那么这幅图就称之为连通图

-- 图的存储结构:邻接表

1.使用一个大小为V的数组 Queue[V] adj,把索引看做是顶点; 
2.每个索引处adj[v]存储了一个队列,该队列中存储的是所有与该顶点相邻的其他顶点

邻接图.jpg

-- API设计:

API.jpg

-- 代码:

/**
 * 图-无向图
 */
public class Graph {
    // 顶点的数量
    private final int V;
    // 边的数量
    private int E;
    // 邻接表
    private Queue<Integer>[] adj;

    // 构造方法
    public Graph(int v) {
        // 初始化顶点的数量
        this.V = v;
        // 初始化边的数量
        this.E = 0;
        // 初始化邻接表
        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的邻接表队
        adj[v].enqueue(w);
        // 将v添加到w的邻接表
        adj[w].enqueue(v);
        // 边的数量+1
        E++;
    }

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

-- 深度优先搜索

简单来说就是一条路走到黑,没路可走在回来

深度优先.jpg

-- 代码:

/**
 * 无向图-深度优先搜索
 */
public class DepthFirstSearch {
    // 索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;
    // 记录有多少个顶点与s顶点相通
    private int count;

    // 构造深度优先搜索对象,使用深度优先搜索找出图中所有与s顶点相通的顶点
    public DepthFirstSearch(Graph G,int s) {
        // 创建一个长度与图顶点数一样大小的布尔数组
        marked = new boolean[G.V()];
        // 搜索图中与顶点相通的所有顶点
        dfs(G,s);
    }

    // 使用深度优先搜索找出图中顶点v的所有相邻顶点
    private void dfs(Graph G,int v) {
        // 把当前顶点标记为已搜索
        marked[v] = true;
        // 遍历顶点v的邻接表,得到每一个顶点w
        for (Integer w : G.adj(v)) {
            // 如果当前顶点没有被搜索过,则递归搜索与顶点w相通的其他顶点
            if(!marked[w]) {
                dfs(G,w);
            }
        }
        // 相通的顶点数量+1
        count++;
    }

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

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

-- 广度优先搜索

广度优先搜索.jpg

-- 代码:

/**
 * 无向图-广度优先搜索
 */
public class BreadthFirstSearch {
    // 顶点是否被搜索标识
    private boolean[] marked;
    // 记录相通顶点数
    private int count;
    // 待搜索顶点队列
    private Queue<Integer> waitSearch;

    // 构造方法
    public BreadthFirstSearch(Graph G,int s) {
        // 初始标识
        marked = new boolean[G.V()];
        // 初始化相通数
        count = 0;
        // 初始化待搜索顶点队列
        waitSearch = new Queue<Integer>();

        // 使用广度优先搜索,找出所有与s顶点相通的顶点数量
        bfs(G,s);
    }

    // 使用广度优先搜索,找出所有与s顶点相通的顶点数量
    private void bfs(Graph G,int s) {
        // 标记当前顶点为已搜索
        marked[s] = true;
        // 当前顶点加入待搜索队列
        waitSearch.enqueue(s);
        // 从待搜索队列去处顶点,搜索其邻接表
        while(!waitSearch.isEmpty()) {
            Integer wait = waitSearch.dequeue();
            // 遍历邻接表
            for (Integer v : G.adj(wait)) {
                // 如果当前顶点未被搜索,则递归搜索这个顶点
                if(!marked[v]) {
                    bfs(G,v);
                }
            }
        }
        // 相通数量+1
        count++;
    }

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

    // 返回相通顶点数量
    public int count() {
        return count;
    }
}

-- 路径查找:

1、用深度优先搜索,将路径保存在edgeTo数组中,数组中保存的是每个顶点的上一个顶点。
2、遍历edgeTo数组(路径),将结果加入栈中返回,栈中就是从起点到我们要查找的顶点的路径
/**
 * 无向图-路径查找
 */
public class DepthFirstPaths {
    // 顶点是否与被搜索标记
    private boolean[] marked;
    // 起点
    private int s;
    // 图的所有路径
    private int[] edgeTo;

    // 构造方法
    public DepthFirstPaths(Graph G,int s) {
        // 初始化顶点是否被搜索标记
        marked = new boolean[G.V()];
        // 初始化起点
        this.s = s;
        // 初始化图的路径
        edgeTo = new int[G.V()];

        // 使用深度优先搜索,找出图的路径
        dfs(G,s);
    }

    // 使用深度优先搜索,找出图的路径
    private void dfs(Graph G,int v) {
        // 标记当前路径已被搜索
        marked[v] = true;
        // 遍历当前顶点邻接表
        for (Integer w : G.adj(v)) {
            // 如果当前顶点未被搜索,则递归查找当前顶点邻接表
            if(!marked[w]) {
                edgeTo[w] = v;  // 保存路径,原理是保存从上一个顶点到该顶点的路径
                dfs(G,s);
            }
        }
    }

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

    // 找出从起点到顶点w的路径
    public Stack<Integer> pathTo(int w) {
        // 判断一下,w到s之间是否存在路径
        if(!hasPath(w)) {
            return null;
        }

        // 创建一个栈保存路径
        Stack<Integer> stack = new Stack<>();
        // 遍历路径
        for(int v = w;v != s;v = edgeTo[v]) {
            stack.push(v);
        }
        // 将起点加入栈中
        stack.push(s);
        return stack;
    }
}

@ 以上内容属于个人笔记