尚硅谷-数据结构与算法-章图

 

 

 

 

 

 

 

 

//深度优先遍历算法
    //i 第一次就是0
    private void dfs(boolean[] isVisited, int i) {
        //1.首先,我们访问该结点,就是输出
        System.out.print(getValueByIndex(i) + "->");
        //访问完后将该结点设置成已经访问
        isVisited[i] = true;
        //2.查找结点v(i)的第一个邻接结点w
        int w = getFirstNeighbor(i);
        //3.若w存在,则执行4,如果w不存在,则回到第一步,将从v的下一个结点继续
        while (w != -1) {//说明存在,然后得先判断是否被访问郭
            //4.若w未被访问,则对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤1,2,3)
            if (!isVisited[w]) {//
                dfs(isVisited, w);
            }
            //如果已经被访问过
            //5.查找结点v的w的邻接结点的下一个邻接结点,转到步骤3
            w = getNextNeighbor(i, w);
        }
    }

    //对dfs进行重载,对应w不存在的情况
    public void dfs() {
        isVisited = new boolean[5];
        //遍历所有的节点,进行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }

 

 

//对一个结点进行广度优先遍历的方法
    private void bfs(boolean[] isVisited,int i){
        int u;//表示队列的头节点对应的下标
        int w;//表示邻接结点的w,对应的下标
        //队列,记录结点访问的顺序
        LinkedList queue = new LinkedList();

        //访问结点,输出结点信息
        System.out.print(getValueByIndex(i)+"->");
        //访问完后标记为已经访问
        isVisited[i]=true;
        //将结点加入队列
        queue.addLast(i);//队列的特点:加入的时候从尾部加入,取的时候从头部开始取

        while (! queue.isEmpty()){//只要队列非空,就一直进行
            //取出队列头节点的下标
            u=(Integer)queue.removeFirst();//自动拆箱
            //得到第一个邻接点的下标w
            w=getFirstNeighbor(u);
            while (w!=-1){//说明找到
                //判断是否访问过
                if (!isVisited[w]){//未访问,直接输出
                    System.out.print(getValueByIndex(w)+"->");
                    //标记为已访问
                    isVisited[w]=true;
                    //入队
                    queue.addLast(w);
                }
                //如果已经访问过,那么还是以u为前驱找w后面的下一个邻接点。
                w=getNextNeighbor(u,w);//这个地方就体现出广度优先
            }//此时,在第一个层走到D不通了,这个while循环退出,再重新从外层大循环开始走,先判断队列是否为空,那么u变成1
        }
    }
    //遍历所有的结点,都进行广度优先遍历搜索
    public void bfs(){
        isVisited = new boolean[5];
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]){
                bfs(isVisited,i);
            }

        }
    }

 完整代码1:

package com.atguigu.graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

/**
 * 图的创建与遍历
 * 图的遍历一般有两种策略:1,深度优先遍历 2广度优先遍历
 * 深度优先遍历:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点
 * 比如:v1-v2-v3,先访问v1,然后访问v2,然后以v2为当前结点(当作第一个结点)访问v3,如果v1还和v3相连也是先访问v2
 * 优先向纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
 * 显然是一个递归的过程
 * 算法步骤:
 * 1.访问初始结点v,并标记点v已经访问。
 * 2.查找结点v的第一个结点w
 * 3.若w存在,则执行4,如果w不存在,则回到第一步,将从v的下一个结点继续(这里感觉应该是v的上一个结点进行访问)
 * 4.若w未被访问,则对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤1,2,3)
 * 5.查找结点v的w的邻接结点的下一个邻接结点,转到步骤3
 * 如图具体路径应该是ABCBDBEBA
 * B
 * C    D   E
 * A
 *
 * 二.广度优先的算法步骤;这个就相当于一层一层的遍历,就是先把第一层能访问的全部访问过来一遍
 * 1.访问初始节点v并标记为v已经访问
 * 2.结点v入队列
 * 3.当队列为空时,继续执行,否则算法结束
 * 4.出队列,取得队头节点u
 * 5.查找结点u的第一个邻接点w
 * 6.若结点u的邻接点w不存在,则转到步骤3;否则执行一下三个步骤
 *  6.1. 若结点w尚未被访问,则访问结点w并标记为已访问
 *  6.2. 结点w入队列
 *  6.3. 查找结点u的即w邻接结点后的下一个邻接结点w,转到步骤6
 *
 *
 * @author WZ
 * @create 2021-11-17 20:45
 */
public class Graph {
    public static void main(String[] args) {

        //测试一把图是否创建正确
        int n = 5;//结点的个数
        String[] vertexs = new String[]{"A", "B", "C", "D", "E"};
        //创建图的对象
        Graph graph = new Graph(n);
        //循环的添加结点
        for (String vertex : vertexs) {
            graph.vertexList.add(vertex);
        }
        //添加边的操作
        //A-B A-C B-C B-D B-E 这几个相连A0,B1,C2,D3,E4
        graph.insertEdge(0, 1, 1);//这个表示A-B已经相连接
        graph.insertEdge(0, 2, 1);//A-C
        graph.insertEdge(1, 2, 1);//B-C
        graph.insertEdge(1, 3, 1);//B-D
        graph.insertEdge(1, 4, 1);//B-E
        //显示一把邻接矩阵
        graph.showGraph();

        //测试深度遍历
        System.out.println("深度遍历");
        graph.dfs();
        System.out.println();
        //测试广度优先遍历
        System.out.println("广度优先遍历测试");
        graph.bfs();


    }


    private ArrayList<String> vertexList;//存储顶点的集合
    private int[][] edges;//存储图对应的邻结矩阵
    private int numOfEdges;//表示边的数目
    //定义一个数组boolean[],记录某个结点是否被访问
    private boolean[] isVisited;

    //深度优先算法的实现
    //得到第一个临界结点的下标w;在矩阵中先找到某一行,再找到对应的列。。从左往右一直找
    public int getFirstNeighbor(int index) {//index表示当前结点的下标,指的是行指的是要出发的结点,j是遍历的列,指的是要到的结点,如果存在返回对应的下标,
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[index][j] > 0) {
                return j;
            }
        }
        return -1;
    }
    //这个主要是这样的,比如先从,A开始,在A的那一行往右走,第一个是0,没有关系不输出,然后继续往后走
    //数字为1,说明A和B有关系,B是A的邻接点,然后可以输出B,此时B已经被访问需要在数组设成已经访问,
    //然后来到B所在的行!!!!!这个是深度遍历重点,然后往右走,第一个是1,说明A是邻接点,但是A已经
    //被访问了,继续往右走,0,不行找到第三个数字为1,说明C是B的邻接点,然后记录C已经被访问。
    //然后找到C所在的行,继续从左往右走,A,B虽然都是1但是已经被访问过,然后C没有邻接点了,然后退回到B
    //B从左往右走,一直走到D是邻接点(前面已经访问过了)。。。依此类推。
        /*  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]
          */
    //根据前一个邻接结点的下标来获取下一个邻接结点

    /**
     * @param v1 指的是V1行
     * @param v2 指的是V2列,并且V2是V1的邻接结点,即edges[v1][v2]==1,这个方法是找v1的下一个邻接结点,所以j=v2+1
     *           上面那个只能得到第一个邻接结点的下标。而这个和那个不同,尤其对于邻接结点已经被访问,不能直接用上面那个
     * @return
     */
    public int getNextNeighbor(int v1, int v2) {
        for (int j = v2 + 1; j < vertexList.size(); j++) {
            if (edges[v1][j] > 0) {
                return j;
            }
        }
        return -1;
    }

    //深度优先遍历算法
    //i 第一次就是0
    private void dfs(boolean[] isVisited, int i) {
        //1.首先,我们访问该结点,就是输出
        System.out.print(getValueByIndex(i) + "->");
        //访问完后将该结点设置成已经访问
        isVisited[i] = true;
        //2.查找结点v(i)的第一个邻接结点w
        int w = getFirstNeighbor(i);
        //3.若w存在,则执行4,如果w不存在,则回到第一步,将从v的下一个结点继续
        while (w != -1) {//说明存在,然后得先判断是否被访问郭
            //4.若w未被访问,则对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤1,2,3)
            if (!isVisited[w]) {//
                dfs(isVisited, w);
            }
            //如果已经被访问过
            //5.查找结点v的w的邻接结点的下一个邻接结点,转到步骤3
            w = getNextNeighbor(i, w);
        }
    }

    //对dfs进行重载,对应w不存在的情况
    public void dfs() {
        isVisited = new boolean[5];
        //遍历所有的节点,进行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }



    //对一个结点进行广度优先遍历的方法
    private void bfs(boolean[] isVisited,int i){
        int u;//表示队列的头节点对应的下标
        int w;//表示邻接结点的w,对应的下标
        //队列,记录结点访问的顺序
        LinkedList queue = new LinkedList();

        //访问结点,输出结点信息
        System.out.print(getValueByIndex(i)+"->");
        //访问完后标记为已经访问
        isVisited[i]=true;
        //将结点加入队列
        queue.addLast(i);//队列的特点:加入的时候从尾部加入,取的时候从头部开始取

        while (! queue.isEmpty()){//只要队列非空,就一直进行
            //取出队列头节点的下标
            u=(Integer)queue.removeFirst();//自动拆箱
            //得到第一个邻接点的下标w
            w=getFirstNeighbor(u);
            while (w!=-1){//说明找到
                //判断是否访问过
                if (!isVisited[w]){//未访问,直接输出
                    System.out.print(getValueByIndex(w)+"->");
                    //标记为已访问
                    isVisited[w]=true;
                    //入队
                    queue.addLast(w);
                }
                //如果已经访问过,那么还是以u为前驱找w后面的下一个邻接点。
                w=getNextNeighbor(u,w);//这个地方就体现出广度优先
            }//此时,在第一个层走到D不通了,这个while循环退出,再重新从外层大循环开始走,先判断队列是否为空,那么u变成1
        }
    }
    //遍历所有的结点,都进行广度优先遍历搜索
    public void bfs(){
        isVisited = new boolean[5];
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]){
                bfs(isVisited,i);
            }

        }
    }






    //基本准备
    //构造器
    public Graph(int n) {//n表示构建这个图有多少个顶点
        //初始化矩阵和vertexList
        edges = new int[n][n];
        vertexList = new ArrayList<>(n);
        numOfEdges = 0;//初始化为0

    }

    //插入节点
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }
    //添加边

    /**
     * @param v1     表示点的下标,即,第几个顶点,比如:描述AB之前的关系,A对应的是0,B对应的为1,在矩阵中
     * @param v2     表示第二个顶点的下标
     * @param weight 这个权值表示这条边对应的权值,在矩阵中,要么是1,要么是0,0表示二者有无关联,1表示二者有关联
     */
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }

    //图中常用的方法
    //返回节点的个数
    public int getNumOfVertex() {
        return vertexList.size();
    }

    //得到边的数目
    public int getNumOfEdges() {
        return numOfEdges;
    }

    //返回结点i(下标)对应的数据:0返回的是A,1返回的是B,2返回的是C
    public String getValueByIndex(int i) {
        return vertexList.get(i);
    }

    //返回v1和v2的权值
    public int insertVertex(int v1, int v2) {
        return edges[v1][v2];
    }

    //显示图对应的矩阵
    public void showGraph() {
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }
    }


}

完成代码2:

package com.atguigu.exer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

/**
 * @author WZ
 * @create 2021-11-30 23:13
 */
public class GraphExer2 {
    public static void main(String[] args) {
        GraphExer2 graph=new GraphExer2(5);
        String[] s1=new String[]{"A","B","C","D","E"};
        for (int i = 0; i < s1.length; i++) {
            graph.addDot(s1[i]);
        }
        graph.addBian(0,1,1);
        graph.addBian(0,2,1);
        graph.addBian(1,0,1);
        graph.addBian(1,2,1);
        graph.addBian(1,3,1);
        graph.addBian(1,4,1);
        graph.show();
        graph.dfs();
        System.out.println();
        System.out.println("广度优先");
        graph.bfs();

    }
    //属性
    private ArrayList<String> listDot;
    private  int[][] arrDot;
    private int numofBian;
    private boolean[] isFind;
    //构造器
    public GraphExer2(int n){
        listDot=new ArrayList(n);
        arrDot=new int[n][n];
        numofBian=0;
    }
    //结点的添加方法
    public void addDot(String s){
        listDot.add(s);
    }
    //添加边的方法
    public void addBian(int v1,int v2,int weight){
        arrDot[v1][v2]=weight;
        arrDot[v2][v1]=weight;
        numofBian++;
    }
    //根据i得到相应的值的方法
    public String getDot(int i){
        return listDot.get(i);
    }
    //得到边的数目的方法
    public int getNumofBian(){
        return numofBian;
    }
    //得到结点数目的方法
    public int getNumofDot(){
        return listDot.size();
    }
    //数组的遍历
    public void show(){
        for (int[] ints : arrDot) {
            System.out.println(Arrays.toString(ints));
        }
    }
    //得到邻接点w的方法
    public int getW(int index){
        for (int j = 0; j < getNumofDot(); j++) {
            if (arrDot[index][j]>0){
                return j;
            }
        }
        return -1;
    }
    //的到相邻数据的方法
    public int getNight(int v1,int v2){
        for (int j = v2+1; j < getNumofDot(); j++) {
            if (arrDot[v1][v2]>0){
                return j;
            }
        }
        return -1;
    }
    //深度遍历的方法
    public void dfs(boolean[] isFind,int i){
        System.out.print(getDot(i));
        isFind[i]=true;
        int w=getW(i);
        if (w!=-1){
            if (!isFind[w]){
                dfs(isFind,w);
            }
            w=getNight(i,w);
        }
    }
    //对深度遍历进行重载
    public void dfs(){
        isFind=new boolean[5];
        for (int i = 0; i < getNumofDot(); i++) {
            if (!isFind[i]){
                dfs(isFind,i);
            }
        }
    }
    //广度优先遍历的算法
    public void bfs(boolean[] isFind,int i){
        int u;
        int w;
        LinkedList queue=new LinkedList();
        System.out.println(getDot(i));
        isFind[i]=true;
        queue.addLast(i);
        while (!queue.isEmpty()){
            u=(Integer)queue.removeFirst();
            w=getW(u);
            while (w!=-1){
                if (!isFind[w]){
                    System.out.println(getDot(w));
                    isFind[w]=true;
                    queue.addLast(w);
                }
                w=getNight(u,w);
            }
        }
    }
    public void bfs(){
        isFind=new boolean[5];
        for (int i = 0; i < getNumofDot(); i++) {
            if (!isFind[i]){
                bfs(isFind,i);
            }
        }
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值