图深度优先遍历算法(Java实现)

∥☆過路亽.° 提交于 2019-11-30 01:58:56
算法思路
  • 节点有多个后继节点时先遍历边权重小的
  1. 指定一个起始节点
  2. 把当前节点所有后继节点按路径权值升序排序
  3. 取出第一个后继节点,以此节点为起始节点重复第二步
  4. 若当前节点没有后继节点,则回溯到仍有未访问的后继节点的节点处重复第三步

算法实现

图的实现
  • 此实现方法没有节点类
  • 用枚举类型UNDISCOVERD表示未被发现,VISITED表示已被访问
  • 采用邻接矩阵和顶点索引
  • 邻接矩阵int[][] matrix(邻接矩阵无需设置为沿对角线对称)
    • matrix[i][j]表示从索引i的节点指向索引j的节点的权值
    • 权值为0表示两点不连接或者自身与自身不连接
public class Graph<T> {
    private int N; // 节点个数
    public int[][] matrix;  // 邻接矩阵
    private Status[] statuses;  // 保存每个节点的状态
    private T[] datas;  // 保存每个节点的数据
}
enum Status {  // 节点对象的状态
    // 未被发现, 已被遍历
    UNDISCOVERD, VISITED
}
重点
  • 找出所有后继节点
    • index是当前节点索引
    • List<Integer> toVisit保存当前节点可能遍历的后继节点的索引
    • 循环遍历每个节点,查询邻接矩阵,如果matrix[index][j] > 0则说明当前节点有一条边指向索引j节点,找到一个后继节点,加到toVisit集合
        List<Integer> toVisit = new ArrayList<>();  
        for (int j = 0; j < N; j++) {
            if (matrix[index][j] > 0 && statuses[j] != Status.VISITED) {
                toVisit.add(j);  // 添加到待遍历列表
            }
        }
  • 把当前节点所有后继节点按路径权值升序排序
    • 集合toVisit调用sort()方法需要重写compare()方法(默认升序)
    • o1 和o2 是两个对象也即两节点索引,return matrix[index][o1] - matrix[index][o2]的作用是,集合调用sort()方法进行排序时,按前当前节点指向索引o1节点的边权重减去当前节点指向索引o2节点的边权重,小于0(前一条边的权重小)则两条边的位置不变,大于0则交换位置(大概意思是这样)
        // 找出当前节点到所有待遍历的节点中权值最小的一个
        toVisit.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return matrix[index][o1] - matrix[index][o2];
            }
        });
  • 方法参数是节点索引
    • 取出最小权重的后继节点,用这个后继节点索引递归
    • 按权重顺序,并且判断该节点状态是否为VISITED,不是才递归
        // 对当前节点所有后继节点遍历
        for (int i : toVisit) {
            if (statuses[i] != Status.VISITED) {
                depthFirstTravel(i);
            }
        }
具体步骤

depthFirstTravel(int index)

  1. 将传进来索引对应节点状态设置为已访问VISITED
  2. 输出节点保存数据datas[index]
  3. 声明List<Integer> toVisit保存某个节点可能遍历的后继节点的索引
  4. 循环遍历找出后继节点
  5. 后继节点按权重排序
  6. 按顺序递归
    /**
     * 深度优先遍历
     *
     * @param index 从这个索引的节点开始遍历
     * @return void
     */
    public void depthFirstTravel(int index) {
        // 设置该节点状态为已被遍历
        statuses[index] = Status.VISITED;
        System.out.println(datas[index]);
        List<Integer> toVisit = new ArrayList<>();  // 保存某个节点可能遍历的后继节点的索引
        // 保存当前节点所有后继节点的索引
        for (int j = 0; j < N; j++) {
            if (matrix[index][j] > 0 && statuses[j] != Status.VISITED) {
                toVisit.add(j);  // 添加到待遍历列表
            }
        }
        // 找出当前节点到所有待遍历的节点中权值最小的一个
        toVisit.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return matrix[index][o1] - matrix[index][o2];
            }
        });
        // 对当前节点所有后继节点遍历
        for (int i : toVisit) {
            if (statuses[i] != Status.VISITED) {
                depthFirstTravel(i);
            }
        }
    }

测试

  1. 6个节点,对应保存数据为字母ABCDEF
  2. int[][] set是为了初始化邻接矩阵graph.setMatrix(set[i][0], set[i][1], set[i][2])
  3. 执行深度优先遍历
    public static void main(String[] args) {
        Graph<String> graph = new Graph<>(6);
        graph.setDatas(new String[]{"A", "B", "C", "D", "E", "F"});
        int[][] set = {{0, 1, 1},
                {0, 2, 3},
                {1, 3, 2},
                {1, 5, 2},
                {2, 3, 4},
                {2, 5, 7},
                {3, 4, 1},
                {4, 5, 8}};

        for (int i = 0; i < set.length; i++) {
            graph.setMatrix(set[i][0], set[i][1], set[i][2]);
        }
        graph.depthFirstTravel(0);
    }

图

输出结果:A B D E F C

完整代码
public class Graph<T> {
    private int N; // N个节点
    public int[][] matrix;  // 邻接矩阵
    private Status[] statuses;  // 保存每个节点的状态
    private T[] datas;  // 保存每个节点的数据
    public Graph(int N) {
        this.N = N;
        matrix = new int[N][N];
        statuses = new Status[N];
        datas = (T[]) new Object[N];  // 泛型数组实例化
        initStatuses();
    }
    public void setDatas(T[] datas) {
        this.datas = datas;
    }
    /**
     * 初始化状态数组
     *
     * @return void
     */
    private void initStatuses() {
        for (int i = 0; i < N; i++) {
            statuses[i] = Status.UNDISCOVERD;
        }
    }
    /**
     * 邻接矩阵保存的信息是从一个节点指向另一个节点的信息
     *
     * @param from   从这个节点
     * @param to     指向这个节点
     * @param weight 路径权重
     * @return void
     */
    public void setMatrix(int from, int to, int weight) {
        matrix[from][to] = weight;
    }
    /**
     * 深度优先遍历
     *
     * @param index 从这个索引的节点开始遍历
     * @return void
     */
    public void depthFirstTravel(int index) {
        // 设置该节点状态为已被遍历
        statuses[index] = Status.VISITED;
        System.out.println(datas[index]);
        List<Integer> toVisit = new ArrayList<>();  // 保存某个节点可能遍历的后继节点的索引
        // 保存当前节点所有后继节点的索引
        for (int j = 0; j < N; j++) {
            if (matrix[index][j] > 0 && statuses[j] != Status.VISITED) {
                toVisit.add(j);  // 添加到待遍历列表
            }
        }
        // 找出当前节点到所有待遍历的节点中权值最小的一个
        toVisit.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return matrix[index][o1] - matrix[index][o2];
            }
        });
        // 对当前节点所有后继节点遍历
        for (int i : toVisit) {
            if (statuses[i] != Status.VISITED) {
                depthFirstTravel(i);
            }
        }
    }
    public static void main(String[] args) {
        Graph<String> graph = new Graph<>(6);
        graph.setDatas(new String[]{"A", "B", "C", "D", "E", "F"});
        int[][] set = {{0, 1, 1},
                {0, 2, 3},
                {1, 3, 2},
                {1, 5, 2},
                {2, 3, 4},
                {2, 5, 7},
                {3, 4, 1},
                {4, 5, 8}};

        for (int i = 0; i < set.length; i++) {
            graph.setMatrix(set[i][0], set[i][1], set[i][2]);
        }
        graph.depthFirstTravel(0);
    }
}
enum Status {  // 节点对象的状态
    // 未被发现, 已被遍历
    UNDISCOVERD, VISITED
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!