图以及图的广度优先(DFS)和深度优先遍历(BFS)

在这里插入图片描述
图是一种数据结构,由一些顶点(节点)连接而成。

可以用list来保存节点,numOfEdges保存边的条数,用一个同等大小的Boolean数组表示节点是否被遍历过。用邻接矩阵来表示这个图的关系。
邻接矩阵:在这里插入图片描述
如果两个节点相连则设为1,否则为0,这样便可以构造出一个矩阵。

public class Graph {
    private ArrayList<String> vertexList;//节点
    private int[][] edges;//矩阵
    private int numOfEdges;//边数
    private boolean[] isVisited;//记录是否被访问过

	public Graph(int n){
	        edges = new int[n][n];
	        vertexList = new ArrayList<String>(n);
	        numOfEdges = 0;
	    }

	 //获取结点个数
	    public int getNumOfVertex(){
	        return vertexList.size();
	    }
	
	    //获取边的个数
	    public int getNumOfEdges(){
	        return numOfEdges;
	    }
	
	    //获取结点i对应的数据
	    public String getValByIndex(int i){
	        return vertexList.get(i);
	    }
	
	    //返回权值
	    public int getWeight(int v1, int v2){
	        return edges[v1][v2];
	    }
	
	    //显示图对应的矩阵
	    public void showGraph(){
	        for (int[] i : edges){
	            System.out.println(Arrays.toString(i));
	        }
	    }
 }

根据上面的逻辑可以很快写出添加节点和边的方法
注意:由于存在arraylist中,每个节点都有自己的下标。下面的v1,v2参数就是节点的下标。

//插入节点,每个节点都有下标
    public void insert(String s){
        vertexList.add(s);
    }

    /**
     * 添加边
     * @param v1 表示点的下标
     * @param v2 v2 第二个顶点的下标
     * @param weight 权值
     */
    public void insertEdge(int v1, int v2, int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;//边的条数+1
    }
    

遍历

图的遍历又分成深度遍历(DFS)和广度遍历(BFS)。
首先我们要实现下面两个方法。
在这里插入图片描述
第一个方法目的是获取邻接节点的下标,比如参数是0,代表获取a的第一个邻接节点下标也就是b。
第二个方法是获取邻接节点的下一个邻接节点坐标,比如参数是(0,1),则找到跳过(0,1)节点的(0,2),为邻接节点。

//得到第一个邻接节点下标
    public int getFirstNeighbor(int index){
        for (int j=0; j < vertexList.size(); j++){
            if (edges[index][j] > 0)
                return j;
        }
        return -1;
    }

    //根据前一个邻接节点的下标来获取下一个邻接节点
    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;
    }

至此我们可以写出深度优先遍历
1)从下标为i的节点开始,如果遍历输出过了该节点则把对应的bool数组置为true。
2)然后获取第一个邻接节点,如果存在则以此邻接节点继续递归。
3)如果访问过了该节点则跳过此节点并找到下一个邻接节点的下标。
4)第一个方法结束后仅仅是根据一个节点进行了DFS,所以需要重载此方法,对每个节点进行DFS

//深度优先遍历DFS
    private void DFS(boolean[] isVisited,int i){
        //访问该节点并输出
        System.out.println(getValByIndex(i)+"->");
        isVisited[i] = true;//标记为访问过
        int j = getFirstNeighbor(i);//获取第一个邻接节点的下标
        while (j != -1){
            if ( !isVisited[j]){//如果j未被访问
                DFS(isVisited,j);
            }
            //如果j已经被访问
            j = getNextNeighbor(i,j);
        }
    }

	//DFS的重载,遍历所有的节点
    public void DFS(){
        isVisited = new boolean[vertexList.size()];
        for (int i=0; i < getNumOfVertex(); i++){
            if (! isVisited[i]){
                DFS(isVisited,i);
            }
        }
    }

广度优先遍历则是另一种思路。
1)首先我们需要创建一个队列记录访问节点的顺序。并用u,w记录队列头节点的下标和邻接节点的下标。
2)当输出一个节点后则放入队列尾部。当队列不为空时从队列中删除并获得头节点的坐标,然后获取此节点的邻接节点。
3)如果存在且未被遍历过,则遍历此节点并放入队列尾部。若被遍历过则寻找下一个邻接节点。

  //广度优先BFS,对一个节点
    private void BFS(boolean[] isVisited,int i){
        int u;//队列头节点的下标
        int w;//邻接节点的下标
        LinkedList queue = new LinkedList();//队列记录访问节点的顺序
        System.out.println(getValByIndex(i)+"->");
        isVisited[i] = true;
        queue.addLast(i);
        while (!queue.isEmpty()){
            u = (int) queue.removeFirst();
            w = getFirstNeighbor(u);
            while (w != -1){
                if (!isVisited[w]){
                    System.out.println(getValByIndex(w)+"->");
                    isVisited[w] = true;
                    queue.addLast(w);
                }
                w = getNextNeighbor(u,w);
            }
        }
    }
	//BFS的重载,遍历所有节点
	public void BFS(){
	        isVisited = new boolean[vertexList.size()];
	        for (int i=0; i<getNumOfVertex(); i++){
	            if (!isVisited[i]){
	                BFS(isVisited,i);
	            }
	        }
	    }

在这里插入图片描述

深度优先遍历的思想是先根据y[0]进行遍历,寻找在y[0]行上的所有邻接节点并输出,结束后进行y[1],y[2],y[…]的遍历。

广度优先遍历则是根据y[0]找到邻接节点b后,立马根据b节点进行遍历。也就是未遍历完与a节点的所有邻接节点,立马换到y[1]行,找到b的邻接节点来遍历。同理也只会找到b的一个可遍历的邻接节点就将b放入队列,继续从f开始遍历。由于遍历过后会放入队列,找不到邻接节点后会回溯到队列中的头节点继续遍历。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值