欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据结构与算法练习---图的创建与遍历(深度优先/广度优先)

程序员文章站 2022-05-20 19:06:09
...
package graph;

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

/**
 * 创建图
 */
public class Graph {

    private ArrayList<String> vertexList;  //储存顶点元素的集合
    private int[][] edges;  //储存边使用的二维数组
    private int edgeNum;  //保存边的数量
    private boolean[] isVisit;

    public static void main(String[] args) {
        String[] vertexs = {"A", "B", "C", "D", "E"};
        String[] vertexs1 = {"1", "2", "3", "4", "5", "6", "7", "8"};

        Graph graph = new Graph(5);
        Graph graph1 = new Graph(8);
        //设置顶点
        for (int i = 0; i < vertexs.length; i++) {
            graph.addVertex(vertexs[i]);
        }
        for (int i = 0; i < vertexs1.length; i++) {
            graph1.addVertex(vertexs1[i]);
        }

        //设置边
        graph.addEdeg(0, 1, 1);//A->B
        graph.addEdeg(0, 2, 1);//A->C
        graph.addEdeg(2, 1, 1);//C->B
        graph.addEdeg(1, 3, 1);//B->D
        graph.addEdeg(1, 4, 1);//B->E/
        // 设置边
        graph1.addEdeg(0, 1, 1);
        graph1.addEdeg(0, 2, 1);
        graph1.addEdeg(1, 3, 1);
        graph1.addEdeg(1, 4, 1);
        graph1.addEdeg(3, 7, 1);
        graph1.addEdeg(4, 7, 1);//B->E
        graph1.addEdeg(2, 5, 1);//B->E
        graph1.addEdeg(2, 6, 1);
        graph1.addEdeg(5, 6, 1);

        //展示
        graph.show();
        //深度优先遍历
        System.out.println("深度优先!");
        graph.deepFirstSearch();
        //重置标记数组
        graph.resetVisit();
        System.out.println();
        //广度优先遍历
        System.out.println("广度优先!");
        graph.breadFirstSearch();
        System.out.println("--------------------------------split---------------------------------------");
        //展示
        graph1.show();
        //深度优先遍历
        System.out.println("深度优先!");
        graph1.deepFirstSearch();
        //重置标记数组
        graph1.resetVisit();
        System.out.println();
        //广度优先遍历
        System.out.println("广度优先!");
        graph1.breadFirstSearch();
    }

    //构造器
    public Graph(int n){
        //初始化各个成员变量
        vertexList = new ArrayList<>(n);
        edges = new int[n][n];
        edgeNum = 0;
        isVisit = new boolean[n];
    }

    //根据传入的当前元素的坐标找到其第一个邻接元素  如果有则返回该元素的下标,如果没有则返回-1
    public int getFirstNeighbor(int index){
        for(int i = 0; i < vertexList.size(); i++){
            int temp = edges[index][i];
            if(temp > 0){
                return i;
            }
        }
        return -1;
    }

    //根据前一个邻接结点的索引找到下一个邻接结点的索引
    public int getNextNeighbor(int v1, int v2){
        for (int i = 1 + v2; i < vertexList.size(); i++) {
            if(edges[v1][i] > 0){
                return i;
            }
        }
        return -1;
    }

    /**
     * 提供空参的重载方法
     * 在该方法中提供各个结点的索引回溯遍历
     */
    public void breadFirstSearch(){
        for (int i = 0; i < vertexList.size(); i++) {
            if(!isVisit[i]){
                breadFirstSearch(isVisit , i);
            }
        }
    }

    /**
        根据广度优先算法遍历图
     *  找到初始结点的邻接结点。并将其保存于队列中,从队列中按顺序取出数据,并重复上述步骤完成图的遍历
     */
    public void breadFirstSearch(boolean[] isVisit, int i){
        //输出当前结点,并将其状态改为以访问
        System.out.print(vertexList.get(i) + "=>");
        isVisit[i] = true;
        //声明队列  linkedList也是队列实现类的一种
        LinkedList<Integer> queue = new LinkedList<>();
        //将当前元素的索引加入队列中
        queue.addLast(i);
        //只要队列不为空就继续该循环
        //两个循环为本方法的核心代码  使用两层while循环和一个队列产生了一种类似于递归的效果,可以一层一层向下深入
        while (!queue.isEmpty()){
            //取出当前元素的下标
            Integer u = queue.removeFirst();
            //找出与其相连的下一个元素
            int w = getFirstNeighbor(u);
            //下一个元素不为空则继续该循环
            while (w != -1){
                //若该元素违背访问  则输出该元素  并将其置为以访问状态  并将当前元素加入链表中
                if(!isVisit[w]){
                    System.out.print(vertexList.get(w) + "=>");
                    isVisit[w] = true;
                    queue.addLast(w);
                }
                //若该元素已经被访问过则继续找出与当前相连通的其他元素
                w = getNextNeighbor(u,  w);
            }
        }
    }


    /**
     * 根据深度优先算法遍历图
     * 从一条可行的通路直到最后一个满足要求的结点
     * @param isVisit
     * @param i
     */
    public void deepFirstSearch(boolean[] isVisit , int i){
        //输出当前结点
        System.out.print(vertexList.get(i) + "-->");
        //将当前结点置为以访问
        isVisit[i] = true;
        //循环递归访问其他结点
        int w = getFirstNeighbor(i);
        //只要当前元素还有邻接元素就继续循环
        while (w != -1){
            //只要邻接元素未被访问过就递归调用该方法
            if(!isVisit[w]){
                deepFirstSearch(isVisit , w);
            }
            //若当前邻接元素已被访问过,则查找他的下一个邻接元素只要不为空则继续循环
            w = getNextNeighbor(i , w);
        }
    }

    /**
     * 提供空参的重载方法
     * 在该方法中提供各个结点的索引回溯遍历
     */
    public void deepFirstSearch(){
        for (int i = 0; i < vertexList.size(); i++) {
            if(!isVisit[i]){
                deepFirstSearch(isVisit , i);
            }
        }
    }

    //添加顶点
    public void addVertex(String vertex){
        vertexList.add(vertex);
    }

    //添加边和权值
    public void addEdeg(int v1 , int v2, int weight){
        //创建的图为无向图  故 A->B <=> B->A
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        edgeNum++;
    }

    //返回图的顶点个数
    public int getVertexNum(){
        return vertexList.size();
    }

    //返回图边的个数
    public int getEdgeNum(){
        return edgeNum;
    }

    //返回顶点下标对应的顶点元素
    public String getVertexByIndex(int index){
        return vertexList.get(index);
    }

    //返回v1,v2处对应的权值
    public int getWeight(int v1, int v2){
        return edges[v1][v2];
    }

    //显示邻接矩阵
    public void show(){
        for (int[] edge : edges) {
            System.out.println(Arrays.toString(edge));
        }
    }

    //重置标记数组
    public void resetVisit(){
        for (int i = 0; i < isVisit.length; i++) {
            isVisit[i] = false;
        }
    }
}