Python 每日一记251>>>Java图的广度优先遍历算法

一、什么图的广度优先遍历

在这里插入图片描述
简而言之,就是从指定的第一个节点开始,查找相连的所有节点,这是一层遍历,然后找第一个节点的每个相邻的节点的所有相邻节点,这就是第二层遍历,以此类推,知道遍历完所有的节点,很像二叉树的层序遍历,注意期间需要标记是否被访问过,且需要一个队列来装节点对应的索引。

二、核心思路

  1. 创建一个队列用于装nodelist的索引,cur为当前索引,next为邻接值索引 先装入指定的第一个节点到队列中,打印出这个索引对应的节点,并且标记为被访问过
  2. 在队列不为空时循环,弹出头部索引,以此获得第一个邻接值索引
  3. 判断这个邻接值是否存在,如果这个邻接值存在,判断这个邻接值是否被访问过,如果这个邻接值没有被访问过打印其对应节点,如果这个邻接值被访问过,再找第二个邻接值索引
  4. 这样不断的循环,找每个节点的相邻节点,一层一层的,直到找完

三、Java代码

package mypackage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

//队列类,用链表实现
class Queue<T> implements Iterable<T>{
    //    节点个数,头节点,尾节点
    private int N;
    private Node head;
    private Node last;
    //节点类
    public class Node {
        public T data;
        public Node next;

        public Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }
    //构造方法,初始化
    public Queue() {
        this.N = 0;
        this.head = new Node(null,null);
        this.last = null;
    }
    //队列长度
    public int size(){
        return N;
    }
    //队列是否为空
    public boolean isEmpty(){
        return N==0;
    }
    //入队列
    public void enqueue(T data){
//        如果队列为空,说明尾节点为空,让新节点为尾节点,头借点指向尾节点
        if (isEmpty()){
            last=new Node(data,null);
            head.next=last;
//            如果队列不为空,让新节点为尾节点,老的尾节点指向新尾节点
        }else {
            Node oldlast=last;
            last=new Node(data,null);
            oldlast.next=last;
        }
//        最后元素+1
        N++;
    }
    //出队列,注意先入先出,每次出的节点就是head指向的第一个节点,然后让head只想第二个节点即可
//    且注意,如果队列为空,要将last=null
    public T dequeue(){
//        如果为空,返回null
        if (isEmpty()){
            return null;
//            如果不为空,让head只想第二个节点,元素-1,且如果队列为空,要将last=null
        }else {
            Node oldfirst=head.next;
            head.next=oldfirst.next;
            N--;

            if (isEmpty()){
                last=null;
            }
//            返回弹出的元素
            return oldfirst.data;
        }
    }

    //    遍历
    @Override
    public Iterator iterator() {
        return new QIterator();
    }

    //    创建一个内部类实现Iterator接口
    public class QIterator implements Iterator {
    //        定义一个遍历的节点
        private Node n;
        public QIterator() {
//            初始化为0索引位置
            this.n = head;
        }

        //重写两个方法
        @Override
        public boolean hasNext() {
//            这个方法判断是否超出最大索引,如果超出会停止遍历
            return n.next != null;
        }

        @Override
        public Object next() {
//            这个方法会遍历得每个节点
            n = n.next;
            return n.data;
        }
    }
}

class Graph {
    //    成员变量,nodelist节点集合,如A,B,C----,matrix矩阵,numedge边的数量
    public ArrayList<String> nodelist;
    public int[][] matrix;
    public int numedge;
//    定义一个是否访问过的数组
    public boolean[] isVisited;

    //    构造方法,传入节点个数
//    初始化边为0,初始化矩阵大小为nn,初始化集合大小为n
    public Graph(int n) {
        this.numedge = 0;
        matrix = new int[n][n];
        nodelist = new ArrayList<>(n);
//        这个数组放每个节点是否访问过的状态
        isVisited = new boolean[n];

    }

    //    插入节点
    public void insertnode(String node) {
        nodelist.add(node);
    }

    //    添加边
//    先在矩阵中表示交叉处的值,再将边的数量+1
//    v1,v2是索引,value是0或者1
    public void insertedge(int v1, int v2, int value) {
        matrix[v1][v2] = value;
//        因为是双向的,所以还要matrix[v2][v1]=value;
        matrix[v2][v1] = value;
        numedge++;
    }

    //    返回节点个数
    public int getNumnode(){
        return nodelist.size();
    }
    //    返回边的数量
    public int getNumedge(){
        return numedge;
    }

    //    返回节点i对应的数据
    public String getnode(int i){
        return nodelist.get(i);
    }

    //    返回v1,v2对应的value
    public int getvalue(int v1,int v2){
        return matrix[v1][v2];
    }

    //    显示图
//    循环打印一维数组即可
    public void showgraph(){
        for (int[] arr:matrix) {
            System.out.println(Arrays.toString(arr));
        }
    }

//    广度优先遍历
//    找到某个节点i的第一个相邻节点,注意这里的i对应的是nodelist的索引,也表示矩阵的第几行
//    找到j表示矩阵的第几列,其实也就是nodelist的索引
    public int firstAdjacentNode(int i) {
        for (int j = 0; j < nodelist.size(); j++) {
    //            如果等于1表示有连接
            if (matrix[i][j] == 1) {
                return j;
            }
        }
        return -1;//循环完了都没找到邻接值返回-1
    }

//    根据某个节点v1,v2的值,找到这个节点的相邻节点
//    为什么要设置这个方法呢,因为当找到某个节点的第一个邻接节点时,如果判断这个临界值被访问过
//    就还要再找下一个邻接节点,相当于根据第一个邻接节点的坐标找其邻接节点的索引j,找到j表示矩阵的第几列,其实也就是nodelist的索引
    public int secondAdjacentNode(int v1,int v2) {
//        为什么要j = v2+1,就是从本行的下一列开始循环
        for (int j = v2+1; j < getNumnode(); j++) {
            if (matrix[v1][j] == 1) {
                return j;
            }
        }
        return -1;//循环完了都没找到邻接值
    }

//   深度优先遍历核心方法
//    创建一个队列用于装nodelist的索引,cur为当前索引,next为邻接值索引
//    先装入首节点到队列中,打印出这个索引对应的节点,并且标记为被访问过
//    在队列不为空时循环,其中的程序包括,弹出头部索引,以此获得第一个邻接值索引
//    判断这个邻接值是否存在,如果存在判断是否被访问过,如果没有被访问过打印其节点,如果被访问过,再找第二个邻接值索引
//    这样不断的循环,找每个节点的相邻节点,一层一层的,直到找完
    public void bfs(int i){
        Queue<Integer> index=new Queue<>();
        int cur;
        int next;
        index.enqueue(i);
        System.out.print(getnode(i)+"--");
        isVisited[i]=true;
        while (!index.isEmpty()){
            cur=index.dequeue();
            next=firstAdjacentNode(cur);
            //这个while循环找打所有的邻接节点
            while (next!=-1){
                if(!isVisited[next]){
                    System.out.print(getnode(next)+"--");
                    isVisited[next]=true;
                    index.enqueue(next);
                }else {
                    next=secondAdjacentNode(cur,next);
                }
            }
        }
    }
    public void bfs() {
//        对每一行都进行dfs,学习视频中说需要i的遍历,
//		其实这里我觉得不需要循环每一个,直接传入一个0索引,就会自动遍历完所有的,
//因为只要队列不为空,就会根据每次弹出一个队列中的元素,然后遍历完所有相连的元素,不存在单独的元素不被遍历到,不知道这个想法对不对?
        for (int i = 0; i < getNumnode(); i++) {
            // 如果没有被访问过
            if (!isVisited[i]){
                bfs(i);
            }
        }
    }
}

/*
//    深度优先遍历
//    找到某个节点i的第一个相邻节点,注意这里的i对应的是nodelist的索引,也表示矩阵的第几行
//    找到j表示矩阵的第几列,其实也就是nodelist的索引
    public int firstAdjacentNode(int i) {
        for (int j = 0; j < nodelist.size(); j++) {
//            如果等于1表示有连接
            if (matrix[i][j] == 1) {
                return j;
            }
        }
        return -1;//循环完了都没找到邻接值返回-1
    }

//    根据某个节点v1,v2的值,找到这个节点的相邻节点
//    为什么要设置这个方法呢,因为当找到某个节点的第一个邻接节点时,如果判断这个临界值被访问过
//    就还要再找下一个邻接节点,相当于根据第一个邻接节点的坐标找其邻接节点的索引j,找到j表示矩阵的第几列,其实也就是nodelist的索引

    public int secondAdjacentNode(int v1,int v2) {
//        为什么要j = v2+1,就是从本行的下一列开始循环
        for (int j = v2+1; j < getNumnode(); j++) {
            if (matrix[v1][j] == 1) {
                return j;
            }
        }
        return -1;//循环完了都没找到邻接值
    }

//    开始核心算法,深度优先算法,deep first search
//    这里的i对应的是nodelist的索引,也表示矩阵的第几行
    public void dfs(int i){
//       打印出这个节点,设置为被访问过
        System.out.print(getnode(i)+"--");
        isVisited[i]=true;
//       找下一个邻接节点
        int next=firstAdjacentNode(i);
//       如果找到了,
//       判断是否被访问过,如果没有被访问过递归这个方法
//       如果被访问过,找这个邻接节点的邻接节点,循环while
        while (next!=-1){
            if (!isVisited[next]){
                dfs(next);
            }else {
                next=secondAdjacentNode(i,next);
            }
        }
    }
//对外调用方法,上面的dfs(int i)重载方法才是核心
    public void dfs() {
//        对每一行都进行dfs
        for (int i = 0; i < getNumnode(); i++) {
            // 如果没有被访问过
            if (!isVisited[i]){
                dfs(i);
            }
        }
    }
}
 */
//测试
public class MyJava {

    public static void main(String[] args) {
//        初始化结点个数
        Graph graph = new Graph(5);
//        添加节点
        graph.insertnode("A");
        graph.insertnode("B");
        graph.insertnode("C");
        graph.insertnode("D");
        graph.insertnode("E");
//        添加边
//        连接关系为A-B,A-C,B-D,B-E,注意是双向的
//        其他的连接都是0
        graph.insertedge(0, 1, 1);
        graph.insertedge(0, 2, 1);
        graph.insertedge(1, 2, 1);
        graph.insertedge(1, 3, 1);
        graph.insertedge(1, 4, 1);

//        显示图
        System.out.println("图矩阵如下:");
        graph.showgraph();
//        返回节点的个数
        System.out.println("节点个数:" + graph.getNumnode());
//        返回边的个数
        System.out.println("边个数:" + graph.getNumedge());
//        返回节点i对应的数据
        System.out.println("节点集合中索引0处的节点:" + graph.getnode(0));
//        返回v1,v2对应的value
        System.out.println("矩阵中,索引(1,0)处的值:" + graph.getvalue(1, 0));

//        优先深度遍历
//        graph.dfs();
//        广度优先遍历
        graph.bfs();
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值