【数据结构与算法】Graph 图的深度遍历和广度遍历

【数据结构与算法】Graph 图的深度及广度遍历


前言

什么是图

本文不赘述图的相关概念。简单的阐述一下个人对图的理解:图(Graph)是一中逻辑的数据关系,可以是使用数组(邻接矩阵)、数组加链表(邻接表)的形式保存和展示。
图分为有向和无向,也就是元素之间的邻接是否有方向;也分为有权与无权值,也就是元素之间的权值(可以理解为重庆到云南1000公里,这个1000就是权值)。

邻接矩阵就是一个二维数据来表示,其中arr[0][1] = 1 ,及表示0号元素和1号元素是相接的,这里0和1 ,一般情况是一个数组中元素的下标。值为1一般表示权值,如arr[重庆][云南] =1000,这个1000就是值两地的权值。一般情况下0代表自己如arr[1][1] = 0、不邻接一般用一个数字表示(自定),本问中,自己和不邻接在存储时都用0表示。
在这里插入图片描述

在这里插入图片描述

一、图的深度遍历

1、遍历其实就是,需要把这个图所包含的元素全部遍历出来。我在自己想的时候,可以想到使用递归的方式遍历。其实就符合深度遍历的概念

 private ArrayList<String> vertexList;//存储顶点
    private int[][] edges; //存储图对应的矩阵关系
    private int munOfEdges; //边的个数
    private boolean[] isVisited ;  //用于记录哪一个节点是已经被访问了

    public static void main(String[] args) {
        int i = 5;
        NoWayGraphArray noWayGraphArray = new NoWayGraphArray(i);
        String[] vertexs = {"A","B","C","D","E"};
        for (String vertex : vertexs) {
            noWayGraphArray.insertVertex(vertex);
        }
        
		//这里只是在手动生产一张图
        noWayGraphArray.insertEdge(0,1,1);
        noWayGraphArray.insertEdge(1,2,1);
        noWayGraphArray.insertEdge(0,2,1);
        noWayGraphArray.insertEdge(1,3,1);
        noWayGraphArray.insertEdge(1,4,1);

        noWayGraphArray.showGraph();
        noWayGraphArray.BFS();
    }

    public NoWayGraphArray(int n) {
        edges = new int[n][n] ;
        vertexList = new ArrayList<>(n);
        munOfEdges = 0;
        isVisited = new boolean[n];
    }
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    /**
     * 整个数据是用来表示,list元素中的关系的 使用一个矩阵来表示
     * @param v1 表示当前元素在list中的下标  A->0
     * @param v2 表示第二个元素在list中的下标 b->1
     * @param weight 如果元素相连就是1   不相连接就是0   这个历史无向图,所以A<->b,所以赋值两次
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        munOfEdges++;
    }

    public int getNumOfVertex(){
        return vertexList.size();
    }

    public int getNumOfEdges(){
        return munOfEdges;
    }

    public String getValueByIndex(int i){
        return vertexList.get(i);
    }

    public int getWeight(int v1,int v2){
         return  edges[v1][v2];
    }


    public void showGraph(){
        for (int[] ints : edges){
            System.out.println(Arrays.toString(ints));
        }
    }

最后的结果
例A B C D E
A [0, 1, 1, 0, 0]
B [1, 0, 1, 1, 1]
C [1, 1, 0, 0, 0]
D [0, 1, 0, 0, 0]
E [0, 1, 0, 0, 0]

完成了图的基础创建之后,开始深度遍历(depth first search )

1.思路

1、因为元素会被访问多次,需要一个标识来记录访问的情况boolean[]
2、深度遍历的逻辑,①首先选择一个顶点V,通过一个方法来获取到该顶点的最近一个邻接元素w②获取到w之后,将其作为新的顶点,获取w最近的邻接元素。这里已经开始递归了,这里要分如果有元素就判断,这个元素是不是已经访问过,没有重复②。如果没有或者已经访问过了,需要去找v中相对于w的下一个元素。(这里有点绕,我用数组在阐述一次,————(一开始我们进入递归,传入索引为0的第一个元素最为顶点,也就是A,找到A在矩阵中最近的一个邻接元素,就是B,arr[0][1] = 1 ,第一次访问B,弹出B,在用B的索引值1,继续递归,如果B的邻接元素全部遍完,或者没有邻接元素,这一个栈顶出栈之后,在最开始的A需要继续访问B之后的下一个邻接元素))

    //图的DFS
    public void DFS(){
        for (int i =0 ;i<vertexList.size();i++){
            if (!isVisited[i]){
                System.out.print(vertexList.get(i)+"->");
                this.DFS(i);
            }
        }
    }

    public void DFS(int i ){
        this.isVisited[i] = true;
        int w = this.getNextNeighbor(i);
        while (w != -1){
            if (!isVisited[w]){
                System.out.print(vertexList.get(w)+"->");
                this.DFS(w);
            }
            w = getNextNeighbor(i,w);
        }
    }

    /**
     *  这里就是从二维数组的矩阵中,确定纵轴的元素下标。arr[index][遍历该数组],找到第一个weight不为0的下标。
     * @param index 从某个顶点开始,,找第一个临界点
     * @return
     */
    public int getNextNeighbor(int index){
        for (int i = 0; i < vertexList.size(); i++) {
            if (edges[index][i]>0){
                return i;
            }
        }
        return -1;
    }

    /**
     * 这个方式是用于,当A->B时,当B作为本次递归的顶点时,需要排除A和已经扫描的节点,向后继续遍历
     * @param index 需要获取到下一个节点的顶点
     * @param v 需要排除的当前节点,逻辑需要找V,后面的一个链接的节点
     * @return
     */
    public int getNextNeighbor(int index ,int v){
        for (int i = v + 1; i <vertexList.size(); i++) {
            if (edges[index][i]>0)
                return i;
        }
        return -1;
    }

二、广度遍历

1.思路

1、整体思路是和深度相反的,这里创建一个LInkedList,用于存放在遍历某一个顶点时,获取到的所有的为访问的元素,再用循环遍历这个链表,不断弹出该链表的元素,进行遍历。最外层也需要一个循环来保证,左右的元素都遍历到

代码如下(示例):

    public void BFS(){
        for (int i = 0; i < vertexList.size(); i++) {
            BFS(i);
        }
    }

    public void BFS(int i){
        LinkedList<Integer> queue = new LinkedList<>();
        int n,w;
        if (!isVisited[i]){
            System.out.print(vertexList.get(i)+"->");
        }
        isVisited[i]= true;
        queue.add(i);
        while (!queue.isEmpty()){
            n = queue.removeFirst();
            w = getNextNeighbor(n);
            while (w != -1){
                if (!isVisited[w]){
                    System.out.print(vertexList.get(w)+"->");
                    queue.add(w);
                    isVisited[w] = true;
                }
                w = getNextNeighbor(n,w);
            }
        }
    }

总结

以上就是图的深度和广度遍历的,个人想法和学习记录,欢迎大家指正!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值