Java超详细精讲数据结构之bfs与双端队列

Java超详细精讲数据结构之bfs与双端队列

什么是BFS?

BFS 是一种广度优先搜索的算法,与其对应的是 DFS (深度优先搜索) 算法。

BFS 的思想是从一个起始状态开始,一层一层向外扩散,直到扩散到目标状态为止。

具体的实现方式是使用队列来存储要扩散的状态,在每次扩散时,先将队首元素出队,然后将该状态的所有子状态入队。入队的操作会保证每个状态只被扩散一次,从而确保BFS的正确性。

在BFS 中,还有一个重要的概念——广度,表示的是从起点到该状态的路径长度,即扩散到该状态需要的步数。扩散到目标状态时,该步数也就是最少步数。这也是 BFS 算法的一个重要应用场景——搜索最短路径问题。

双端队列

双端队列是一种具有队列和栈的特点的线性数据结构。

它支持在队列头部和尾部进行插入和删除操作,因此可以同时支持FIFO和LIFO两种操作方式,使得双端队列在某些算法中更易于使用。

使用Java语言实现双端队列时,我们可以使用LinkedList来实现,它已经实现了Deque接口。

BFS与双端队列的结合

BFS算法在执行过程中,需要不断将状态加入队列并出队,这显然需要使用队列这种数据结构。但是,当需要在队列头部和尾部进行插入和删除操作时,使用普通的队列就显得不太方便。

这时,双端队列就派上用场了——可以将队列定义为双端队列,然后在需要在队列头部和尾部进行操作时,直接调用双端队列的方法即可。

在具体的实现中,我们可以将每个要扩散的状态封装为一个类,然后定义一个变量表示该状态的广度,方便后续的最短路径查询。

下面是一个BFS算法与双端队列的示例代码:

import java.util.*;

public class BFS{

    static class State {
        int num;
        int depth;
        State(int num, int depth) {
            this.num = num;
            this.depth = depth;
        }
    }

    public static void main(String[] args) {
        Deque<State> deque = new LinkedList<>();
        Set<Integer> visited = new HashSet<>();

        deque.offer(new State(0, 0));
        visited.add(0);

        while (!deque.isEmpty()) {
            State p = deque.poll();
            if (p.num == 10) {
                System.out.println(p.depth);
                return;
            }

            int num1 = p.num + 1;
            if (!visited.contains(num1)) {
                visited.add(num1);
                deque.offer(new State(num1, p.depth + 1));
            }

            int num2 = reverse(p.num);
            if (!visited.contains(num2)) {
                visited.add(num2);
                deque.offer(new State(num2, p.depth + 1));
            }
        }
    }

    private static int reverse(int num) {
        int res = 0;
        while (num != 0) {
            res = res * 10 + num % 10;
            num /= 10;
        }
        return res;
    }
}

在上面的示例中,我们通过 BFS 算法求解了从0到10的最短路径,并使用了双端队列来实现队列的操作。

再举一个更实际的例子,比如我们需要在一个迷宫里搜索从起点到终点的最短路径。这时,我们可以将每一个位置都看作是一个状态,然后使用 BFS 算法来搜索。

在具体的实现中,我们可以使用双端队列来存储要进行扩散的状态,从而方便地在队头和队尾进行操作。

import java.util.*;

public class BFS {

    static class Pos {
        int x, y;
        Pos(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public static void main(String[] args) {
        int[][] maze = {
                {0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 1, 1, 1, 0, 1, 0},
                {0, 0, 0, 0, 0, 0, 1, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 1, 0, 0, 0, 0, 0, 0},
                {0, 1, 0, 1, 1, 1, 1, 0},
                {0, 1, 0, 0, 0, 0, 0, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 0, 0, 0, 0, 0, 0, 0}
        };
        int n = maze.length, m = maze[0].length;
        Pos start = new Pos(1, 6);
        Pos end = new Pos(7, 1);

        Deque<Pos> deque = new LinkedList<>();
        Set<String> visited = new HashSet<>();

        deque.offer(start);
        visited.add(start.x + "," + start.y);

        int[][] dirs = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};

        while (!deque.isEmpty()) {
            Pos p = deque.poll();
            for (int[] dir : dirs) {
                int x = p.x + dir[0];
                int y = p.y + dir[1];
                if (x >= 0 && x < n && y >= 0 && y < m && maze[x][y] == 0) {
                    String pos = x + "," + y;
                    if (!visited.contains(pos)) {
                        visited.add(pos);
                        deque.offer(new Pos(x, y));
                    }
                }
            }
            if (p.x == end.x && p.y == end.y) {
                System.out.println("找到终点");
                return;
            }
        }

        System.out.println("无法到达终点");
    }
}

在上述示例中,我们使用了 BFS 算法和双端队列,来搜索从起点到终点的最短路径。为了方便表示每个状态,我们使用了 Pos 类来存储位置信息,并使用了一个字符串形式的 visited 集合来表示已经访问过的状态。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java超详细精讲数据结构之bfs与双端队列 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • 什么是直接内存?

    直接内存(Direct Memory)是一种在 Java 中使用 NIO(New Input/Output)时可以使用的内存区域。直接内存不受 Java 堆大小的限制,可以使用操作系统的内存,因此可以提高 I/O 操作的效率。 Java 中,可以使用 ByteBuffer 类来操作直接内存。 以下是使用直接内存的完整使用攻略: 分配直接内存 Java 中,可…

    Java 2023年5月12日
    00
  • java实现输出任意整数的每一位

    下面是java实现输出任意整数的每一位的完整攻略。 步骤一:将整数转成字符串 我们知道,字符串中每个字符都可以通过下标访问。所以,我们只需要将整数转换成字符串,就可以通过下标依次访问每个数字了。 int num = 123456; String numStr = String.valueOf(num); // 将整数转换成字符串 步骤二:遍历字符串,输出每一…

    Java 2023年5月26日
    00
  • 解决java字符串转换成时间Unparseable date出错的问题

    当将一个Java字符串转换为时间对象时,有时候会出现“Unparseable date”(无法解析日期)的错误,这是非常常见的错误。通常情况下,这个问题是由于日期字符串与SimpleDateFormat模式字符串不匹配造成的。下面是解决此问题的完整攻略。 步骤1:确定日期格式 首先,需要确定原始日期的格式。在Java中,使用SimpleDateFormat类…

    Java 2023年5月20日
    00
  • springdata jpa使用Example快速实现动态查询功能

    下面是Spring Data JPA使用Example快速实现动态查询功能的完整攻略。 什么是Spring Data JPA Spring Data JPA 是Spring框架的一项子项目,它基于 Hibernate 实现了 JPA 规范,提供了一种简化 JPA 数据访问层的方法。 利用Spring Data JPA实现动态查询 使用Spring Data …

    Java 2023年5月20日
    00
  • Java关于jar包的知识详解

    让我来为你详细讲解Java关于jar包的知识。 什么是jar包? jar是Java Archive的缩写,意思是Java压缩文件。它是Java中常用的一种打包方式,相当于将多个class文件或其它文件合并成一个文件,并对其中的文件进行压缩以减小体积。 jar包的优点 方便代码管理:将多个class文件或其它文件合并到一起,方便管理和分发。 便于发布和部署:只…

    Java 2023年5月20日
    00
  • 让Java代码更高效

    让Java代码更高效的完整攻略包含以下几个方面: 1.避免不必要的对象创建 在Java的运行时环境中,对象的创建是非常昂贵的,因为需要对内存进行动态分配和回收。因此,在Java编程过程中应该避免频繁地创建对象,尤其是在循环中。 例如,下面代码创建了一个StringBuilder对象,并在循环中进行了多次的字符串拼接操作: String str = &quot…

    Java 2023年5月20日
    00
  • Java Druid连接池与Apache的DBUtils使用教程

    Java Druid连接池与Apache的DBUtils使用教程 简介 Java 连接池是一种在应用程序中重用数据库连接的技术,它能够有效地提高应用程序的性能和资源利用率。Druid 是阿里巴巴开源的高性能 Java 数据库连接池库,提供了比常见开源数据库连接池更为丰富的功能。DBUtils 是 Apache 开源的轻量级 JDBC 工具库,它提供了简单方便…

    Java 2023年6月16日
    00
  • Java文件选择对话框JFileChooser使用详解

    Java文件选择对话框JFileChooser使用详解 JFileChooser Java 文件选择对话框 (JFileChooser) 是 Java Swing 组件库中的一部分。它允许用户选择文件或目录,是一种常用的用户界面组件。 JFileChooser 核心属性 下面是 JFileChooser 的一些核心属性: currentDirectory: …

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部