数据结构复习之图的入门(深度优先搜索和广度优先搜索递归实现)

数据结构之图的入门

一、图的定义及分类

定义:图是有一组顶点一组能够将两个顶点相连的边组成的

特殊的图:

  • 自环:即一个顶点有一条连接自己的边
  • 平行边:连接同一对顶点的边
    图的分类:
  • 按照连接两个顶点的边的不同,图可以分为两种
    • 无向图:边仅仅连接两个顶点,没有方向
    • 有向图:边不仅连接两个顶点,并且具有方向

1.1 无向图

图的相关术语:

  • 相邻顶点:当两个顶点通过一条边相连时,我们称这两个顶点是相邻的,并且称这条边依附于这两个顶点
  • 度:某个顶点的度就是依附于该顶点的边数
  • 子图:是一幅图中所有边的子集(包含这些依附的顶点)组成的图
  • 路径:由边顺序连接的一组顶点组成
  • 环:是一条至少含有一条边且终点和起点相同的路径
  • 连通图:如果图中的任意两个顶点之间都存在路径可以到达,那么这个图就称为连通图
  • 连通子图:一个非连通图由若干部分组成,每个连通的部分都可以称为该图的连通子图

图的存储结构:

要表示一幅图,需要表示清楚图中的所有顶点所有连接顶点的边

常见的图的存储结构有两种:邻接矩阵和邻接表

  • 邻接矩阵:
    • 使用二维数组表示
    • 将顶点看为索引
    • 将边看为两个索引对应的元素的值为1右边,0无边
    • 表示图的空间复杂度为n^2
  • 邻接表:
    • 使用一位数组和链表组成
    • 每个索引处存储一个队列,该队列中存储的是与该顶点相连的其他顶点
    • 空间复杂度小于n^2,后面会使用邻接表的形式表示图

1.2 手搓图

package GraphTest;


import LinearTest.QueueTest;

public class Graph {
    //顶点数目
    private final int V;
    //边的数目
    private int E;
    //邻接表
    private QueueTest<Integer>[] adj;
    public Graph(int V){
        //初始化顶点数量
        this.V = V;
        this.E = 0;
        this.adj = new QueueTest[V];
        for(int i = 0;i<adj.length;i++){
            adj[i] = new QueueTest<Integer>();
        }
    }
    //获取顶点数目
    public int V(){
        return V;
    }
    //获取边的数目
    public int E(){
        return E;
    }
    //向图中添加一条边,传入两个点,在两点间建立一条边
    public void addEdge(int v,int w){
        //把w添加到v的链表中,这样就有一条从v->w的边
        adj[v].enqueue(w);
        //把v添加到w的链表中,这样就有一条从w->v的边
        adj[w].enqueue(v);
        //边的数目++
        E++;
    }
    //获取和顶点v相邻的所有顶点
    public QueueTest<Integer> adj(int v){
        return adj[v];
    }
}

1.3 图的搜索

深度优先搜索DFS:

对一个连通图进行遍历的算法。思想:从顶点V0开始,沿着一条路径走,如果走到最后发现不能到达目标解,则返回上一个顶点,换一条路径继续走

不清楚,上代码debug跟几遍就OK

package GraphTest;

public class DepthFirstSearch {
    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;
    //记录有多少个顶点与s顶点相通
    private int count;
    //构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点对应的所有相邻顶点
    public DepthFirstSearch(Graph G,int s){
        //创建一个对应所有顶点的是否搜索标记数组
        marked = new boolean[G.V()];
        //搜索G图所有与s相连的顶点
        dfs(G,s);
    }
    //深度优先遍历算法,传入要搜索的图和顶点
    private void dfs(Graph G,int s){
        //把当前顶点设置为已搜索,因为是无向图,防止重复搜索
        marked[s] = true;
        //开始查找s对应的邻接表中有边的结点
        for (Integer w : G.adj(s)) {
            //如果w结点未被搜索过,则递归调用该结点搜索
            if (!marked[w]){
                dfs(G,w);
            }
        }
        count++;
    }
    //判断w顶点与s顶点是否相通
    public boolean marked(int w){
        return marked[w];
    }
    //获取与顶点s相通的所有顶点的总数
    public int count(){
        return count;
    }

    public static void main(String[] args) {
        Graph G = new Graph(13);
        G.addEdge(0,5);
        G.addEdge(0,1);
        G.addEdge(0,2);
        G.addEdge(0,6);
        G.addEdge(5,3);
        G.addEdge(5,4);
        G.addEdge(3,4);
        G.addEdge(4,6);
        DepthFirstSearch search = new DepthFirstSearch(G,0);
        //测试与某个顶点相通的顶点数量
        int count = search.count();
        System.out.println("与起点0相通的顶点的数量为:"+count);
    }
}

广度优先搜索:

对一个连通图进行遍历的算法。思想:从顶点V0开始,把所有连通顶点都遍历一次,没有找到解,就再从通顶点中找出一个递归以上过程
这里的QueueTest是自己复习时写的队列类,大家可以用jdk自带的Queue类

package GraphTest;

import LinearTest.QueueTest;

/** * 广度优先遍历 * 原理:对一个顶点的所有连通结点进行遍历,之后再深入每一个连通的顶点递归该过程 */
public class BreadthFirstSearch {
    private boolean[] marked;
    //记录右多少个顶点与s顶点相通
    private int count;
    //用来保存待搜索邻接表的点
    /*回顾树的层次遍历,使用一个辅助队列,弹出一个元素, 就把该元素的子结点放入队列*/
    private QueueTest<Integer> waitSearch;
    Integer next;
    //构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相通顶点
    public BreadthFirstSearch(Graph G,int v){
        //创建一个数组,标记图中顶点是否已被搜索
        marked = new boolean[G.V()];
        //创建辅助队列
        waitSearch = new QueueTest<Integer>();
        //调用广度优先搜索算法
        waitSearch.enqueue(v);
        bfs(G,v);
    }

    /** * bfs算法需要做的事 * 1、把当前顶点设置为已搜索,放入队列,弹出 * 2、遍历出当前顶点邻接表中所有相邻的顶点 * 3、放入辅助队列中 * 4、弹出元素重复以上过程 */
    private void bfs(Graph G,int v){
        marked[v] = true;
        boolean flag = false;
        //判断队列是否存在数据
        while (!waitSearch.isEmpty()){
            Integer wait = waitSearch.dequeue();
            for (Integer w : G.adj(wait)) {
                if (!flag && !marked[w]){
                    next = w;
                    flag = true;
                }
                if(!marked[w]){
                    //递归调用广度优先搜索算法,可以将每个连通的顶点放入辅助队列中
                    marked[w] = true;
                    waitSearch.enqueue(w);
                }
            }
            count++;
            bfs(G,next);
        }
    }
    public int count(){
        return count;
    }
    //判断w顶点与s顶点是否相通
    public boolean marked(int w){
        return marked[w];
    }
    public static void main(String[] args) {
        //准备Graph对象
        Graph G = new Graph(13);
        G.addEdge(0,5);
        G.addEdge(0,1);
        G.addEdge(0,2);
        G.addEdge(0,6);
        G.addEdge(5,3);
        G.addEdge(5,4);
        G.addEdge(3,4);
        G.addEdge(4,6);

        G.addEdge(7,8);

        G.addEdge(9,11);
        G.addEdge(9,10);
        G.addEdge(9,12);
        G.addEdge(11,12);
        //准备广度优先搜索对象
        BreadthFirstSearch search = new BreadthFirstSearch(G, 0);
        //测试与某个顶点相通的顶点数量
        int count = search.count();
        System.out.println("与起点0相通的顶点的数量为:"+count);
        //测试某个顶点与起点是否相同
        boolean marked1 = search.marked(5);
        System.out.println("顶点5和顶点0是否相通:"+marked1);
        boolean marked2 = search.marked(7);
        System.out.println("顶点7和顶点0是否相通:"+marked2);
    }
}
全部评论

相关推荐

11-29 11:21
门头沟学院 Java
总包48.5w,意想不到的价格
想开了的垂耳兔很喜欢拱白菜:转人工
点赞 评论 收藏
分享
M_bao:换个排版吧哥们,看着费劲
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-26 18:54
说等下个版本吧的发呆爱好者很贪睡:佬最后去了哪家呀
点赞 评论 收藏
分享
评论
1
收藏
分享
牛客网
牛客企业服务