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

算法思路
  • 节点有多个后继节点时先遍历边权重小的
  1. 指定一个起始节点
  2. 把当前节点所有后继节点按路径权值升序排序并加入到集合中
  3. 按顺序弹出后继节点,将此节点的所有未被遍历的后继节点按权值升序排序加入集合
  4. 重复弹出集合首元素以及把该元素后继节点加入集合直到集合为空
算法实现
图的实现
  • 此实现方法没有节点类
  • 用枚举类型UNDISCOVERD表示未被发现,VISITED表示已被访问
  • 采用邻接矩阵和顶点索引
  • 邻接矩阵int[][] matrix(邻接矩阵无需设置为沿对角线对称)
    • matrix[i][j]表示从索引i的节点指向索引j的节点的权值
    • 权值为0表示两点不连接或者自身与自身不连接
  • List<Integer> toVisit保存整个遍历过程待访问的节点
public class Graph<T> {
    private int N; // 节点个数
    public int[][] matrix;  // 邻接矩阵
    private Status[] statuses;  // 保存每个节点的状态
    private T[] datas;  // 保存每个节点的数据
    private List<Integer> toVisit = new ArrayList<>();  // 保存整个遍历过程待访问的节点
}
enum Status {  // 节点对象的状态
    // 未被发现, 已被遍历
    UNDISCOVERD, VISITED
}
重点
  • 将当前节点的后继节点按权值排序并加入集合
    • 遍历每个节点,查看邻接矩阵
      如果matrix[index][i] > 0则说明当前节点有边指向索引i的节点,然后再判断该节点是否被访问过,跳过被访问过的节点
    • 按权值排序需要重写compare()方法(默认升序)
      o1 和o2 是两个对象也即两节点索引,return matrix[index][o1] - matrix[index][o2]的作用是,集合调用sort()方法进行排序时,按前当前节点指向索引o1节点的边权重减去当前节点指向索引o2节点的边权重,小于0(前一条边的权重小)则两条边的位置不变,大于0则交换位置(大概意思是这样)
  • 将当前节点后继节点的集合合并addAll()到整个遍历过程的待访问集合
    this.toVisit是图对象的成员变量,记录整个遍历过程的待访问节点
    toVisit是当前方法的局部变量记录当前节点的后继待访问节点
    • 如果不用局部变量toVisit而只用this.toVisit则会出现排序时打乱了上一个节点的后继节点的顺序,影响遍历
            // 将当前节点的所有后继节点存入队列
            for (int i = 0; i < N; i++) {
                if (matrix[index][i] > 0 && statuses[i] == Status.UNDISCOVERD) {
                    // 设置该节点状态为已被遍历
                    statuses[i] = Status.VISITED;
                    toVisit.add(i);
                }
            }
            toVisit.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return matrix[index][o1] - matrix[index][o2];
                }
            });
            this.toVisit.addAll(toVisit);
算法主体方法
  • 将第一个节点索引加入this.toVisit并设置为已访问
  • 只要集合this.toVisit不为空,则循环执行
    • 局部变量List<Integer> toVisit保存当前节点的后继节点
    • 弹出集合第一个元素并输出节点储存的数据datas[index]
    • 遍历每个节点找出当前节点的后继节点
    • 将当前节点的所有后继节点存入集合toVisit并设置为已访问VISITED
    • toVisit按权值排序
    • 合并集合this.toVisit.addAll(toVisit)
    /**
     * 广度优先遍历
     *
     * @return void
     */
    public void breadthFirstTravel() {
        this.toVisit.add(0);
        statuses[0] = Status.VISITED;
        while (!this.toVisit.isEmpty()) {  // 只要待访问的队列不为空
            // 弹出本节点
            int index = this.toVisit.remove(0);
            System.out.println(datas[index]);
            // 保存当前节点的后继节点(新建一个toVisit是为了防止排序时影响上一轮的排序)
            List<Integer> toVisit = new ArrayList<>();
            // 将当前节点的所有后继节点存入队列
            for (int i = 0; i < N; i++) {
                if (matrix[index][i] > 0 && statuses[i] == Status.UNDISCOVERD) {
                    // 设置该节点状态为已被遍历
                    statuses[i] = Status.VISITED;
                    toVisit.add(i);
                }
            }
            toVisit.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return matrix[index][o1] - matrix[index][o2];
                }
            });
            this.toVisit.addAll(toVisit);
        }
    }
测试
  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.breadthFirstTravel();
    }

图

输出结果:A B C D F E

完整代码
public class Graph<T> {
    private int N; // N个节点
    public int[][] matrix;  // 邻接矩阵
    private Status[] statuses;  // 保存每个节点的状态
    private T[] datas;  // 保存每个节点的数据
    private List<Integer> toVisit = new ArrayList<>();  // 保存整个遍历过程待访问的节点
    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;
    }
    /**
     * 广度优先遍历
     *
     * @return void
     */
    public void breadthFirstTravel() {
        this.toVisit.add(0);
        statuses[0] = Status.VISITED;
        while (!this.toVisit.isEmpty()) {  // 只要待访问的队列不为空
            // 弹出本节点
            int index = this.toVisit.remove(0);
            System.out.println(datas[index]);
            // 保存当前节点的后继节点(新建一个toVisit是为了防止排序时影响上一轮的排序)
            List<Integer> toVisit = new ArrayList<>();
            // 将当前节点的所有后继节点存入队列
            for (int i = 0; i < N; i++) {
                if (matrix[index][i] > 0 && statuses[i] == Status.UNDISCOVERD) {
                    // 设置该节点状态为已被遍历
                    statuses[i] = Status.VISITED;
                    toVisit.add(i);
                }
            }
            toVisit.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return matrix[index][o1] - matrix[index][o2];
                }
            });
            this.toVisit.addAll(toVisit);
        }
    }
    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.breadthFirstTravel();
    }
}
enum Status {  // 节点对象的状态
    // 未被发现, 已被遍历
    UNDISCOVERD, VISITED
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值