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

yizhihongxing

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日

相关文章

  • Android下拉阻尼效果实现原理及简单实例

    下面我将详细讲解“Android下拉阻尼效果实现原理及简单实例”的完整攻略。 Android下拉阻尼效果实现原理 原理介绍 下拉阻尼效果是指在下拉列表或者下拉刷新等场景中,当用户下拉视图时,视图能够随手指的滑动而进行拉伸或缩放,形成一种类似于弹簧效果的体验。 实现该效果的原理是利用滑动事件监听,根据手指滑动时的位移量以及速度,计算出视图需要滑动的距离,然后对…

    Java 2023年5月23日
    00
  • 浅谈jsp九大内置对象及四个作用域

    浅谈 JSP 九大内置对象及四个作用域 JSP(JavaServer Pages)是一种动态服务器端网页语言,其灵活性在页面交互中得到了广泛应用。在 JSP 页面中,有着九大内置对象及四个作用域的概念。理解这些概念,能够帮助我们更好地使用 JSP 来实现我们的业务逻辑。下面分别进行详细讲解。 九大内置对象 request request 对象封装了客户端 H…

    Java 2023年6月15日
    00
  • Spring常用配置及解析类说明

    下面是“Spring常用配置及解析类说明”的详细攻略。 1. Spring常用配置 1.1 XML配置 Spring框架最初是以XML配置为主的,XML配置的方式包括声明bean和对bean进行依赖注入两个方面。 1.1.1 声明bean 在XML配置文件中,声明bean的方式如下: <bean id="beanId" class=…

    Java 2023年5月19日
    00
  • Go iota 常量基本语法介绍

    Go iota 常量基本语法介绍 Go中的常量是不可变的量,它们被赋值后不能再次更改。常量的值可以在编译时确定,并且它们具有比变量更严格的类型检查。 在Go语言中,有一个特殊的常量生成器叫做iota,可以用来创建一组枚举类型的常量。iota常量生成器初始化为0,并且每次使用后自动加1,一般在常量组中使用。 接下来我们将详细介绍Go iota常量的基本语法。 …

    Java 2023年5月26日
    00
  • Spring Boot实现登录验证码功能的案例详解

    Spring Boot实现登录验证码功能的案例详解 简介 最近,我在开发一个基于Spring Boot的Web应用程序时,需要实现一个登录验证码功能,以确保用户输入有效并防止暴力破解。在研究后,我发现可以通过添加一些依赖项和编写一些代码来轻松地实现此功能。在本文中,我将与您分享实现登录验证码功能的详细步骤。 步骤 步骤1:添加依赖 为了实现登录验证码功能,我…

    Java 2023年5月20日
    00
  • Java构造代码块,静态代码块原理与用法实例分析

    当我们创建Java对象时,会自动对对象进行初始化。除了对属性进行初始化外,我们还可以利用代码块来进行初始化操作。其中Java构造代码块和静态代码块都是常见的初始化方式。 构造代码块 构造代码块是一种在类中直接使用非静态代码块的方式来对实例进行初始化的机制。它只跟随构造函数一起执行,例如: public class CodeBlockExample { { S…

    Java 2023年5月23日
    00
  • Spring Security认证器实现过程详解

    Spring Security认证器实现过程详解 Spring Security是用于保护Web应用程序的开放源代码框架。它可以提供基于角色的安全性,对用户进行身份验证和访问控制来保护应用程序。本文将详细介绍Spring Security认证器实现的过程。 一. Spring Security认证器 Spring Security提供了一个框架来处理所有We…

    Java 2023年6月3日
    00
  • Java线程池高频面试题总结

    Java线程池高频面试题总结 线程池是什么 线程池是一种用于管理多个线程的机制,它能够根据应用程序需要动态地增减线程。线程池在执行完任务后并不会立即销毁线程,而是将线程放入池中等待下一次使用。线程池通常会预先准备好一定数量的线程,这些线程被称为核心线程,在需要时更多的线程将被创建。 为什么使用线程池 线程池有以下优点: 减少线程创建的开销: 创建线程需要花费…

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