Java算法之BFS,DFS,动态规划和贪心算法的实现

Java算法之BFS、DFS、动态规划和贪心算法的实现

本文将详细讲解Java中BFS、DFS、动态规划和贪心算法的实现及使用场景。

BFS

BFS全称Breadth-First Search,即广度优先搜索。BFS算法主要应用于无权重图的最短路径查找,或者非加权的图上的搜索问题。BFS算法使用了队列的数据结构来辅助实现,具体实现步骤如下:

  1. 将起始节点加入队列;
  2. 从队列头取出一个节点,遍历它的所有未被访问过的邻居并标记为已访问;
  3. 将邻居节点加入队列;
  4. 如果队列不为空,则重复步骤2~3。

下面是一个BFS示例代码,用于计算在一个无权重图(以邻接矩阵方式存储)中从给定的起始节点到目标节点的最短路径:

public class BFS {
    public static void main(String[] args) {
        int[][] graph = {
                {0, 1, 1, 0, 0},
                {1, 0, 0, 1, 0},
                {1, 0, 0, 1, 1},
                {0, 1, 1, 0, 1},
                {0, 0, 1, 1, 0}
        };
        int start = 0, end = 4;
        System.out.println("The shortest path from " + start + " to " + end + " is " + bfs(graph, start, end));
    }

    public static int bfs(int[][] graph, int start, int end) {
        Queue<Integer> queue = new LinkedList<>();
        boolean[] visited = new boolean[graph.length];
        queue.add(start);
        visited[start] = true;
        int level = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int curr = queue.poll();
                if (curr == end) {
                    return level;
                }
                for (int j = 0; j < graph.length; j++) {
                    if (graph[curr][j] == 1 && !visited[j]) {
                        queue.offer(j);
                        visited[j] = true;
                    }
                }
            }
            level++;
        }
        return -1;
    }
}

DFS

DFS全称Depth-First Search,即深度优先搜索。DFS算法主要应用于图的遍历问题,具体实现步骤如下:

  1. 从起始节点开始,每次选择一个未被访问的邻居节点进行访问,并将其标记为已访问;
  2. 当前节点的所有邻居节点都被访问过后,则回溯到上一个未被访问的节点继续遍历。

下面是一个DFS示例代码,用于在一个无向图(以邻接表方式存储)中查找从起始节点到目标节点的路径:

public class DFS {
    static boolean det = false;
    static LinkedList<Integer>[] adjList;

    public static void main(String[] args) {
        adjList = new LinkedList[4];
        for (int i = 0; i < 4; i++) {
            adjList[i] = new LinkedList<>();
        }
        adjList[0].add(1);
        adjList[0].add(2);
        adjList[1].add(2);
        adjList[2].add(0);
        adjList[2].add(3);
        int start = 2, end = 3;

        visited = new boolean[4];
        visited[start] = true;
        dfs(start, end);
        if(det){
            System.out.println("Path exists");
        }else {
            System.out.println("Path does not exist");
        }
    }

    static boolean[] visited;

    public static void dfs(int node, int end) {
        if (!det) {
            if (node == end) {
                det = true;
            }
            Iterator<Integer> i = adjList[node].listIterator();
            while (i.hasNext()) {
                int n = i.next();
                if (!visited[n]) {
                    visited[n] = true;
                    dfs(n, end);
                }
            }
        }
    }
}

贪心算法

贪心算法(Greedy Algorithm)是一种在每一步取当前状态下最优解的算法,即贪心算法每次寻找当前最优解,并加以选择和保存。简单理解是尽可能地选择当前情况下最优的方案,从而达到全局最优的效果。贪心算法主要应用在需要求最优解的组合问题,具体实现步骤如下:

  1. 初始解为空解,即其中不包含任何一个原问题提出的元素;
  2. 在满足约束条件的前提下,确定该问题的最大加法递推性质所要求的元素并加入到当前解中;
  3. 使用该元素生成一个新问题,再从该新问题出发重复步骤2~3。

这里给出奶酪问题(LeetCode 882)的贪心算法示例代码:

假设有一个二维平面上的无限大的奶酪,上面散布着许多点,每个点表示一个奶酪。你的任务是找到一条线,这条线会将奶酪切成两份,并且两份奶酪大小相等。回答这条线为多长。奶酪的密度是均匀的,并且两者密度相同。

public class CheeseProblem {
    public static void main(String[] args) {
        int[][] cheese = {{1, 0}, {0, 1}, {1, 1}};
        System.out.println(cheeseSlice(cheese));
    }

    public static double cheeseSlice(int[][] cheese) {
        int n = cheese.length;
        double totalX = 0.0, totalY = 0.0;
        for (int[] point : cheese) {
            totalX += point[0];
            totalY += point[1];
        }
        double midX = totalX / n, midY = totalY / n;
        double[] distances = new double[n];
        for (int i = 0; i < n; ++i) {
            distances[i] = Math.sqrt((cheese[i][0] - midX) * (cheese[i][0] - midX) + (cheese[i][1] - midY) * (cheese[i][1] - midY));
        }
        Arrays.sort(distances);
        double sum1 = 0.0, sum2 = 0.0;
        for (int i = 0; i < n / 2; ++i) {
            sum1 += distances[i];
        }
        for (int i = n / 2; i < n; ++i) {
            sum2 += distances[i];
        }
        return Math.abs(sum1 - sum2);
    }
}

动态规划

动态规划(Dynamic Programming)是算法设计中的一种方法,该方法通常用于优化递归算法,例如求斐波那契数列的值。动态规划算法的基本思想是将原问题拆分为多个子问题,完成一个子问题后保存其结果,便于需要时使用已经完成的子问题的结果计算其他子问题。具体实现步骤如下:

  1. 将原始问题拆分为多个分解的子问题;
  2. 分别解决每个子问题,可以使用记忆化技术从已经计算的子问题中重用计算结果;
  3. 组合每个子问题的解来得到原始问题的解,从而实现对原始问题的求解。

以下是一个斐波那契数列的动态规划示例代码:

public class Fibonacci {
    public static void main(String[] args) {
        int num = 10;
        System.out.print(fibonacci(num));
    }

    public static int fibonacci(int num) {
        int[] memo = new int[num + 1];
        return helper(num, memo);
    }

    public static int helper(int num, int[] memo) {
        if (num <= 1) {
            return num;
        }
        if (memo[num] != 0) {
            return memo[num];
        }
        memo[num] = helper(num - 1, memo) + helper(num - 2, memo);
        return memo[num];
    }
}

以上就是BFS、DFS、贪心算法和动态规划算法的完整攻略,希望可以帮助到读者。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java算法之BFS,DFS,动态规划和贪心算法的实现 - Python技术站

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

相关文章

  • Java实现线程同步方法及原理详解

    Java实现线程同步方法及原理详解 在多线程程序中,线程的并发执行可能导致数据不一致的问题。而线程同步,是为了解决这个问题。本文将详细讲解Java实现线程同步方法及原理。 什么是线程同步 线程同步,就是多个线程尝试访问同一个共享资源时,只有一个线程能够访问该资源,以确保数据的正确性和资源的高效利用。Java通过synchronized关键字实现线程同步。 s…

    Java 2023年5月18日
    00
  • Spring Boot整合mybatis(一)实例代码

    在Spring Boot应用程序中使用MyBatis进行数据库操作是非常常见的。在本文中,我们将介绍如何在Spring Boot应用程序中整合MyBatis,并提供两个示例。 示例一:使用XML配置文件 以下是一个示例,演示如何在Spring Boot应用程序中使用XML配置文件整合MyBatis: 添加依赖 在pom.xml文件中添加以下依赖: <d…

    Java 2023年5月15日
    00
  • Java操作文件输出为字符串以及字符串输出为文件的方法

    对于Java操作文件输出为字符串以及字符串输出为文件的方法,可以分为两个部分进行讲解。 Java操作文件输出为字符串 Java操作文件输出为字符串可以通过以下步骤完成: 打开文件并读取文件内容。 将文件内容转化为字符串。 关闭文件并返回字符串。 以下是Java代码示例: public static String readFile(String filePat…

    Java 2023年5月26日
    00
  • 手写Java LockSupport的示例代码

    下面就手写Java LockSupport的示例代码进行详细讲解。 1. LockSupport简介 在Java中,当一个线程对某个对象的synchronized锁进行等待时,只有主动释放锁的线程或抢占了锁的线程才能解除等待;而LockSupport则是提供了一种更加灵活的线程等待/唤醒机制。LockSupport不需要使用锁和条件变量来实现线程的同步和通信…

    Java 2023年5月30日
    00
  • 详解Java利用深度优先遍历解决迷宫问题

    详解Java利用深度优先遍历解决迷宫问题 简介 在计算机科学中,深度优先遍历是一种用于遍历或搜索树或图的概念。深度优先遍历会先访问深度最大的节点(或者最右边的节点),然后回溯到该节点的父节点,并开始遍历它的另一个子节点。这个过程会一直持续到所有的节点都被访问为止。 用深度优先遍历算法解决迷宫问题可以思路简单易懂,代码编写也相对比较简单。 实现步骤 1. 定义…

    Java 2023年5月19日
    00
  • 详解SpringBoot+Mybatis实现动态数据源切换

    详解SpringBoot+Mybatis实现动态数据源切换 在本文中,我们将详细讲解如何使用SpringBoot和Mybatis实现动态数据源切换。动态数据源切换是指在运行时根据需要切换数据源,而不是在应用程序启动时指定数据源。这种技术可以帮助我们更好地管理多个数据源,并提高应用程序的性能和可扩展性。 环境准备 在开始本文之前,我们需要准备好以下环境: JD…

    Java 2023年5月18日
    00
  • java开发之Jdbc分页源码详解

    首先,我们需要了解JDBC分页的概念,它可以帮助我们在处理大量数据时,避免一次性获取过多的数据,从而提高程序的性能。 下面是一个基于JDBC的分页实现的示例代码,供您参考: import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement;…

    Java 2023年6月16日
    00
  • Java 创建线程的两个方法详解及实例

    Java 创建线程的两个方法详解及实例 在 Java 中,创建线程有两种方法,一种是继承Thread类,另一种是实现Runnable接口。本文将详细介绍这两种方法并提供示例代码。 1. 继承Thread类 继承Thread类是一种创建线程的简单方法,只需要继承Thread类并重写run方法即可。 示例代码: public class MyThread ext…

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