Java Program to Find the Number of Provinces2025年5月6日 | 阅读 9 分钟 “省份数量”问题涉及查找表示为无向图节点的城市组。一个城市组,或称省份,包括直接或间接连接的城市。这个 Java 程序使用深度优先搜索 (DFS) 或并查集等算法来高效地识别和计数这些连通分量。 示例输入 表示城市连接的 4x4 邻接矩阵 1 1 0 0 1 1 0 0 0 0 1 1 0 0 1 1 输出 2 个省份。 解释 2 个省份的输出表示两个连接的城市组。第一组由城市 0 和 1 组成,而第二组由城市 2 和 3 组成。这两个组完全隔离,形成两个不同的省份。 深度优先搜索 (DFS) 遍历方法算法步骤 1:初始化跟踪结构: 创建一个大小为 n(城市数量)的已访问 数组,初始化为 false。此数组用于跟踪已探索过的城市。将 provinceCount 变量设置为 0,用于计算省份数量。 步骤 2:遍历每个城市: 循环遍历所有城市(0 到 n-1)。如果一个城市未被访问,则表示发现了一个新省份。 步骤 2.2:处理已处理的城市: 在开始 DFS 之前,检查该城市是否已被标记为已访问。如果城市已被访问,则表示它已属于先前发现的省份,因此无需再次为该城市启动 DFS。跳过已标记为已访问的任何城市的 DFS 步骤。 步骤 3:使用 DFS 探索连接: 从当前城市开始深度优先搜索 (DFS)。在 DFS 过程中,将当前城市标记为已访问。探索与当前城市直接连接的所有城市。对于每一个未访问的连接城市,递归地执行 DFS。 步骤 3.1:跟踪连接的城市: 在 DFS 遍历过程中,维护一个列表或计数器,用于记录当前省份中已访问的所有城市。这有助于理解哪些城市属于同一个省份,并确保该连通分量中的所有城市都被标记为已访问。一旦当前城市的 DFS 完成,该连通分量中的所有城市都已计算在内。 步骤 4:计算省份: 在完成一个城市及其连接城市的 DFS 后,将 provinceCount 加 1,表示已找到一个完整的省份。 步骤 4.1:优化不连接的城市: 在 DFS 过程中标记一个城市及其连接城市为已访问后,添加一个检查以识别完全不连接的城市(在邻接 矩阵 中没有连接的城市)。如果一个城市未连接到任何其他城市,则将其视为自己的省份。 步骤 5:为剩余城市重复: 继续遍历所有城市。对于已访问的城市,跳过 DFS 步骤。 步骤 5.1:处理边缘情况: 完成所有城市的迭代后:检查由于邻接矩阵中缺少连接而仍未访问的城市。确保算法能够优雅地处理空矩阵或单个城市等情况,并返回正确的省份数量(分别为 0 或 1)。 步骤 6:返回结果: 循环完成后,返回 provinceCount 作为省份总数。 让我们在 Java 程序中实现上述方法。 文件名:NumberOfProvinces.java 输出 2 复杂度分析时间复杂度时间复杂度为 O(n²),其中 n 是城市的数量。这是因为邻接矩阵有 n² 个元素。每个城市都被访问一次。DFS 检查该城市的所有连接,由于遍历期间的嵌套迭代,导致总体二次方复杂度。 空间复杂度空间复杂度为 O(n),其中 n 是城市的数量。这包括用于跟踪已探索过的城市的 visited 数组,以及 DFS 的递归调用栈,在最坏情况下其深度可达 n。邻接矩阵本身已作为输入提供。 方法 2:并查集算法算法步骤 1:初始化父节点和秩数组: 父节点数组:每个城市最初都是自己的领导者。因此,每个城市的父节点最初都是它自己。这有助于跟踪每个城市所属的集合(或省份)。 parent[i] = i 对于每个城市 i。 秩数组:此数组通过将较小的树附加到较大的树上来优化联合操作,从而减小树的深度。这确保了并查集结构保持平衡。 rank[i] = 1 对于每个城市 i。 步骤 2:联合城市: 我们遍历邻接矩阵 (isConnected[i][j]) 以检查哪些城市已连接。如果两个城市 i 和 j 已连接(即 isConnected[i][j] == 1),我们执行联合操作将它们分组到同一个集合(省份)中。联合操作通过更新其根(领导者)城市来合并两个集合。目标是将两个城市链接起来,使它们共享同一个领导者。 按秩合并: 此步骤确保将较小的树附加到较大树的根上,以保持平衡结构。 步骤 3:查找每个城市的根: 为了确定一个城市属于哪个集合,我们使用查找操作。此操作会跟踪城市的父节点(领导者),最终到达其集合的根(即其父节点是它自己的城市)。 路径压缩: 在查找操作期间,我们通过使每个城市直接指向其根来优化结构。这会减小树的深度,从而加快将来的查找操作。 步骤 4:计算唯一省份的数量: 处理完所有城市后,我们需要计算存在多少个唯一的集合(省份)。一个省份是由共享同一根的城市定义的。要计算唯一的省份,我们为每个城市执行查找操作,并计算有多少个唯一的根(领导者)。 步骤 5:返回结果: 唯一根的总数对应于省份的数量。这是我们的最终结果,它告诉我们存在多少个不连接的城市组(省份)。 文件名:NumberOfProvinces.java 输出 2 复杂度分析时间复杂度并查集方法的近似时间复杂度为 O(n²),其中 n 是城市的数量。这是因为我们需要处理具有 n² 个元素的邻接矩阵。由于路径压缩和按秩合并,联合和查找操作的时间复杂度接近常数。 空间复杂度并查集方法的空间复杂度为 O(n),其中 n 是城市的数量。这包括父节点数组和秩数组所需的空间,每个数组的大小都为 n。此外,邻接矩阵作为输入提供,这也占用了空间,但不属于算法的复杂度。 使用 BFS 方法算法步骤 1:初始化: 给定一个表示城市及其连接的邻接矩阵。我们需要跟踪哪些城市已被访问,以避免重复计算同一个省份。为此,我们使用一个 visited 数组,其中每个元素都表示一个城市是否已被访问。 步骤 1.1:开始计算省份: 我们初始化一个 provinceCount 变量为 0。此变量将用于跟踪我们找到的省份数量。 步骤 2:遍历每个城市: 我们循环遍历图中的每个城市。如果一个城市尚未被访问,则表示我们遇到了一个新的省份,因此我们从该城市开始执行 BFS 来探索这个新省份。 步骤 3:从当前城市执行 BFS: 对于每个未访问的城市,我们启动 BFS 搜索。BFS 算法探索与起始城市直接或间接连接的所有城市(从而识别整个连通分量)。在 BFS 过程中,我们在 visited 数组中标记每个已访问的城市。 步骤 3.1:跟踪连接的城市: 在 BFS 遍历过程中,我们维护一个队列来探索与起始城市连接的所有城市。每次我们从队列中取出一个城市时,我们都会在 visited 数组中将其标记。这确保属于同一省份的城市被分组在一起,并且不会在将来的 BFS 遍历中被重新访问。每个城市都被处理一次,确保高效地探索省份内的所有直接和间接连接。 步骤 4:探索所有连接: BFS 使用队列。我们入队起始城市,并开始探索其所有连接的城市。每次我们从队列中取出一个城市时,我们都会检查其连接。如果一个城市已连接(即邻接矩阵中存在 1)并且尚未被访问,我们会将其入队并标记为已访问。 步骤 5:增加省份计数: 在完成一个城市的 BFS 遍历(及其所有连接的城市)后,我们将 provinceCount 加 1,因为我们找到了一个新省份。 步骤 6:为所有城市重复: 我们继续处理所有城市。如果一个城市已被访问,则跳过该城市的 BFS,因为它属于先前发现的省份。 步骤 7:返回结果: 遍历完所有城市后,provinceCount 的值将代表图中省份(连通分量)的总数。然后我们返回 provinceCount。 让我们在 Java 程序中实现上述方法。 文件名:NumberOfProvinces.java 输出 2 复杂度分析时间复杂度程序的 time complexity 为 O(n²),其中 n 是城市的数量。这是因为对于每个城市,我们都使用邻接矩阵检查它与其他所有城市的关系,这会导致每个城市进行 O(n) 次检查,从而导致总体复杂度为 O(n²)。 空间复杂度程序的 space complexity 为 O(n),其中 n 是城市的数量。这包括用于 visited 数组的空间,该数组用于跟踪哪些城市已被探索。此外,BFS 中使用的队列在最坏情况下需要与城市数量成比例的空间。 下一个主题Java 中的 Adam 数 |
在 Java 编程领域,图形用户界面 (GUI) 在提供用户友好和交互式体验方面起着至关重要的作用。GUI 组件是这些界面的构建块,允许开发人员设计和创建复杂的应用程序。在这些组件中,有两个基本概念脱颖而出:...
阅读 3 分钟
统计道路上通过的汽车数量问题只是众多典型算法问题之一,其实际目标是确定在同一条道路上朝相反方向行驶的汽车的有效对的总数。更具体地说,...
5 分钟阅读
图的独立集的先决条件是顶点集,其中没有两个是相邻的。根据定义,它是团的对立面,因此理解图的补集对于继续前进至关重要。本质上,平面图的概念...
阅读 17 分钟
Java.util.function 包在 Java 8 中首次发布,它包含了 DoubleConsumer 接口,用于在 Java 中进行函数式编程。它是一个接受单个 double 值参数但没有任何输出的函数的示例。为了定义其 accept()...
阅读 4 分钟
在 Java 中,当编译器期望一个类定义但遇到其他内容时,会发生“期望类”的错误。这通常是由于缺少花括号、语法错误或关键字放错位置引起的。确保正确的类声明、正确使用数据类型以及保持正确的结构有助于避免这种情况……
7 分钟阅读
在 Java 中,我们使用 int 和 Integer 来存储整数类型的数据。现在,由此产生的问题是,如果两者都用于存储相同类型的数据,那么它们之间有什么区别,为什么我们需要……
阅读 4 分钟
在 Java 中,死锁是多线程的一部分。多线程环境允许我们同时运行多个线程以进行多任务处理。有时线程会发现自己处于永久等待状态,这就是死锁情况。死锁是两个或多个线程尝试...
5 分钟阅读
在线编译器是一个基于云的 IDE,可帮助开发人员在线编译和执行 Java 程序,而无需在本地系统上安装 JDK。在本节中,我们将讨论一些流行的在线 Java 编译器,它们都是免费提供的。流行的在线...
阅读 6 分钟
? LINQ 称为 Language Integrated Query,它出现在 .NET 3.5 和 Visual Studio 2008 中。LINQ 的优点是它能够让 .NET 语言(如 C#、VB.NET 等)创建查询以从数据源中检索数据。对于...
阅读 6 分钟
在直接进入“阻塞队列”主题之前,让我们先简要了解一下队列。队列是对象的有序列表,其中插入发生在列表的尾部,删除发生在列表的前端。因此,它是...
14 分钟阅读
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India