java-图的广度优先遍历(邻接矩阵和邻接表两种存储方式)

本文回顾了广度优先搜索(BFS)算法在邻接矩阵和邻接表两种图表示方式下的实现过程。作者在与经理讨论算法时发现自己对基础知识有所遗忘,因此重新实现了BFS算法,并分享了代码实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自从步入码农的行列,这种基础性的东西就敲得少了,今天和经理讨论算法的时候发现自己有些忘得差不多了,今天特地回来复习复习。于是费了自己一个小时时间,从度娘复习了原理,然后自己摸索着重新实现了一下这个算法。

邻接矩阵:

package com.zhangyanujie.graph.adjacency_matrix;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * @Author zyj
 * @Date 2020/3/27 21:58
 **/
public class BFS {
    private int[][] matrix;
    private char[] vertex;

    public BFS(int[][] matrix,char[] vertex){
        this.matrix = matrix;
        this.vertex = vertex;
    }

    public List<Character> doBFS(char begin){
        List<Character> result = new LinkedList<>();

        // 找到给定字符的位置
        int beginPosition = -1;
        for(int i=0;i<vertex.length;i++){
            if(vertex[i] == begin){
                beginPosition = i;
                break;
            }
        }

        // 用于记录结点是否被访问过
        boolean[] visited = new boolean[vertex.length];

        // 首结点入队
        Queue<Integer> queue = new LinkedList<>();
        queue.add(beginPosition);
        result.add(vertex[beginPosition]);
        visited[beginPosition] = true;

        // 广度优先遍历,当队列不为空时,表示没有遍历完毕
        int currentRow;
        while (!queue.isEmpty()){
            currentRow = queue.poll();
            for(int i=0;i<vertex.length;i++){
                if(matrix[currentRow][i] == 1 && !visited[i]){
                    queue.add(i);
                    result.add(vertex[i]);
                    visited[i] = true;
                }
            }
        }
        return result;
    }

    public void print(){
        System.err.println(Arrays.deepToString(matrix));
        System.err.println(Arrays.toString(vertex));
    }
    
    public static void main(String[] args){
        int[][] matrix = {
                {0,1,1,0,0,0,0,0},
                {1,0,0,1,1,0,0,0},
                {1,0,0,0,0,1,1,0},
                {0,1,0,0,0,0,0,1},
                {0,1,0,0,0,0,0,1},
                {0,0,1,0,0,0,1,0},
                {0,0,1,0,0,1,0,0},
                {0,0,0,1,1,0,0,0},
        };

        char[] vertex = {'A','B','C','D','E','F','G','H'};

        BFS bfs = new BFS(matrix,vertex);
        //bfs.print();

        System.err.println(bfs.doBFS('C'));
    }
}

邻接表:

package com.zhangyanujie.graph.adjacency_list;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * @Author zyj
 * @Date 2020/3/27 22:36
 **/
public class BFS {
    Node[] list;

    public BFS(Node[] list){
        this.list = list;
    }

    public List<Character> doBFS(char begin){
        List<Character> result = new LinkedList<>();

        // 找到给定字符的位置
        int beginPosition = findIndexByValueFromList(begin);


        // 用于记录结点是否被访问过
        boolean[] visited = new boolean[list.length];

        // 首结点入队
        Queue<Integer> queue = new LinkedList<>();
        queue.add(beginPosition);
        result.add(list[beginPosition].value);
        visited[beginPosition] = true;

        // 广度优先遍历,当队列不为空时,表示没有遍历完毕
        Node currentNode;
        int nodePosition;
        while (!queue.isEmpty()){
            currentNode = list[queue.poll()];
            while (currentNode.next != null){
                nodePosition = findIndexByValueFromList(currentNode.next.value);
                if(!visited[nodePosition]){
                    queue.add(nodePosition);
                    result.add(list[nodePosition].value);
                    visited[nodePosition] = true;
                }
                currentNode = currentNode.next;
            }
        }

        return result;
    }

    private Integer findIndexByValueFromList(Character value){
        int location = -1;
        for(int i=0;i<list.length;i++){
            if(list[i].value == value){
                location = i;
                break;
            }
        }
        return location;
    }

    public void print(){
        System.err.println(Arrays.toString(list));
    }

    public static void main(String[] args){
        Node[] list = new Node[]{
                new Node('A',new Node('B',new Node('C',null))),
                new Node('B',new Node('A',new Node('D',new Node('E',null)))),
                new Node('C',new Node('A',new Node('F',new Node('G',null)))),
                new Node('D',new Node('B',new Node('H',null))),
                new Node('E',new Node('B',new Node('H',null))),
                new Node('F',new Node('C',new Node('G',null))),
                new Node('G',new Node('C',new Node('F',null))),
                new Node('H',new Node('D',new Node('E',null))),
        };

        BFS bfs = new BFS(list);

        System.err.println(bfs.doBFS('C'));

        //bfs.print();
    }
}


class Node{
    public Character value;
    public Node next;

    public Node(Character value, Node next) {
        this.value = value;
        this.next = next;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                ", next=" + next +
                '}';
    }
}

 

广度优先遍历(BFS)的时间复杂度取决于所使用的表示方法,常见的有两种形式:邻接矩阵邻接表。 ### 邻接矩阵下的广度优先遍历时间复杂度 当使用邻接矩阵来表示时,空间大小为 \(O(V^2)\),其中 \(V\) 表示顶点的数量。在这种结构下,检查两个节点是否相连只需要常数时间内完成(因为直接通过索引查找)。然而,在进行广度优先遍历时,对于每一个正在处理的节点,我们需要逐一查看所有其他\( V-1 \)个节点,看是否存在边连接它们。因此: - 最坏情况下的时间复杂度是 **\( O(V^2) \)**。 - 这是因为每次都需要扫描一行共 \(V\) 个元素以找到邻居。 - 即便某些位置可能不存在边缘(值为0),仍然需要花时间为每一行做完整次迭代才能确定这一点。 ### 邻接表下的广度优先遍历时间复杂度 如果采用邻接列表来存储形的话,则每个节点只包含与其直接相连的所有其他节点的信息。这样一来, - 访问某个特定节点及其全部邻近节点的操作将在 **线性时间内** 完成——即所需次数与实际存在的边数目相匹配。 - 因此,整体上来说, - 时间复杂度变为 **\( O(E + V) \)** ,这里 \( E \) 是边上数量而不仅仅是最大可能性。 总结一下,在稠密中(大多数节点之间都有直接连线的情况下),两者的表现相近;但是在稀疏里(只有少量的连通关系存在),基于邻接表的方式效率更高因为它能够更准确地反映真实的拓扑特点而不必浪费资源去考察那些事实上并不存在的关系。 #### 示例说明 假设我们有一张由五个城市组成的交通网地,并用上述两种方式进行编码表示: * 使用邻接矩阵表示可能会占用较多的空间,并且即使很多路线实际上并不互通也得预留出相应的位置; * 相比之下,利用邻接列表可以节省大量不必要的内存开销,并且使得查询操作更为高效快捷。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Kirito

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值