深度优先搜索
Posted 浮云神码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度优先搜索相关的知识,希望对你有一定的参考价值。
深度优先搜索在对无环图进行搜索时,在编程时无需考虑顶点重复访问的情况,代码易理解。一旦遇到存在环的图时,深度优先搜索需要额外的空间存储顶点的访问状态,有时访问状态还需要重置...,代码复杂,不易理解,所以关于有环图的题目今后使用其他算法进行解答。
今天分享 1319. 连通网络的操作次数,其中用到一个定理,N个顶点至少需要N-1条边才能互相连接,也可以说N个连通分量至少需要N-1条边才能互相连接。本题通过计算所有顶点的连通分量,间接计算出连通网络的操作次数。
使用栈来实现深度优先搜索时,有部分节点会重复入栈,导致耗时比递归稍长。当然,深度优先搜索并不是解决此问题最好的算法,根据官网的题解,使用并查集时性能更优,等分享并查集算法时,再来比较两种方式的不同。
接下来,分别使用栈和递归实现了两种深度优先搜索算法。
import java.util.*;
/**
* https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected
* 1319. 连通网络的操作次数
* 难度 中等
* 用以太网线缆将n台计算机连接成一个网络,计算机的编号从0到n-1。
* 线缆用connections表示,其中connections[i] = [a, b]连接了计算机a和b。
*
* 网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其他任意一台计算机。
*
* 给你这个计算机网络的初始布线connections,你可以拔开任意两台直连计算机之间的线缆,
* 并用它连接一对未直连的计算机。请你计算并返回使所有计算机都连通所需的最少操作次数。
* 如果不可能,则返回-1 。
* 提示:
*
* 1 <= n <= 10^5
* 1 <= connections.length <= min(n*(n-1)/2, 10^5)
* connections[i].length == 2
* 0 <= connections[i][0], connections[i][1] < n
* connections[i][0] != connections[i][1]
* 没有重复的连接。
* 两台计算机不会通过多条线缆连接。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected
*/
public class NumberOfOperationsToMakeNetworkConnected {
/**
* 通过栈实现
* @param n
* @param connections
* @return
*/
public int makeConnected(int n, int[][] connections) {
// 线缆数量必须大于等于计算机数量 - 1才能有足够的数量把计算机连通
if (connections.length < n - 1) {
return -1;
}
// 初始化邻接表
List<Integer>[] connectionList = new List[n];
for (int i = 0; i < n; i++) {
connectionList[i] = new ArrayList<>();
}
// 无向图, 两个节点互为邻接节点
for (int[] conn : connections) {
connectionList[conn[0]].add(conn[1]);
connectionList[conn[1]].add(conn[0]);
}
// 初始化已访问计算机, 默认全部未访问
boolean[] visited = new boolean[n];
Stack<Integer> stack = new Stack<>();
int result = 0;
// 遍历全部计算机, 一旦发现未访问节点, 说明存在一个连通分量
for (int i = 0; i < n; i++) {
if (!visited[i]) {
stack.push(i);
result++;
}
/**
* 通过栈将当前连通分量中的节点全部标记为已访问
*/
while (!stack.isEmpty()) {
int current = stack.pop();
if (visited[current]) {
continue;
}
visited[current] = true;
List<Integer> list = connectionList[current];
for (Integer neighbor : list) {
if (!visited[neighbor]) {
stack.push(neighbor);
}
}
}
}
// 想要连通N个连通分量, 只需N-1个连接即可
return result - 1;
}
/**
* 使用递归方式实现
* @param n
* @param connections
* @return
*/
public int makeConnected2(int n, int[][] connections) {
// 线缆数量必须大于等于计算机数量 - 1才能有足够的数量把计算机连通
if (connections.length < n - 1) {
return -1;
}
// 初始化邻接表
List<Integer>[] connectionList = new List[n];
for (int i = 0; i < n; i++) {
connectionList[i] = new ArrayList<>();
}
for (int[] conn : connections) {
connectionList[conn[0]].add(conn[1]);
connectionList[conn[1]].add(conn[0]);
}
// 初始化已访问计算机, 默认全部未访问
boolean[] visited = new boolean[n];
// 记录连通分量的数量
int result = 0;
// 遍历全部计算机, 一旦发现未访问节点, 说明存在一个连通分量
for (int i = 0; i < n; i++) {
if (!visited[i]) {
// 增加连通分量数量
result++;
dfs(connectionList, visited, i);
}
}
// 想要连通N个连通分量, 只需N-1个连接即可
return result - 1;
}
/**
* 递归处理邻接点
* @param connectionList 邻接列表
* @param visited 已访问列表
* @param current 当前节点
*/
private void dfs(List<Integer>[] connectionList, boolean[] visited, int current) {
visited[current] = true;
// 将当前节点标记为已访问, 查询邻接节点列表
List<Integer> list = connectionList[current];
for (int neighbor : list) {
// 如果邻接节点未访问过, 重复标记过程
if (!visited[neighbor]) {
dfs(connectionList, visited, neighbor);
}
}
}
以上是关于深度优先搜索的主要内容,如果未能解决你的问题,请参考以下文章