-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
93 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
--- | ||
layout: post | ||
title: 2024-06-04-力扣3067.在带权树网络中统计可连接服务器对数目 | ||
date: 2024-06-04 19:19:11 | ||
excerpt_type: html # <!-- more -->标签渲染方式 | ||
updated: 2024-06-04 | ||
categories: 力扣好题记录 | ||
tags: | ||
- Java | ||
- 算法 | ||
- dfs | ||
- 力扣 | ||
--- | ||
|
||
## 力扣3067.在带权树网络中统计可连接服务器对数目 | ||
|
||
[3067.在带权树网络中统计可连接服务器对数目](https://leetcode.cn/problems/count-pairs-of-connectable-servers-in-a-weighted-tree-network/description/?envType=daily-question&envId=2024-06-04) | ||
|
||
图算法实在是学得太烂了,故记录一个好题 | ||
|
||
<!-- more --> | ||
|
||
这一题其实看完题目能够比较容易地想到一个基础的思路,比较难的是如何确保找到的节点对之间没有公共边。 | ||
|
||
最初的想法是将每两个节点之间的路径记录下来,但是那样实在是过于麻烦了。 | ||
|
||
在灵神题解中,使用基于邻接表的图存储,很轻松地解决了公共节点的问题。对于每个作为根节点`c`的节点,对其每个直接邻接的邻居使用一次`dfs`,同时,在`dfs`中接收起点参数`from`和方向参数`x`,代表从`from`节点开始,向`x`节点方向进行搜索。这实际上相当于将节点`x`作为根节点,遍历其所有的邻居,但是排除掉`from`节点。 | ||
|
||
此外,使用这种方法不需要针对每一个根节点`c`去遍历所有的节点对`(a, b)`,只需要统计在每个方向`x`上有多少个可用节点即可,然后不同方向节点数量相乘就得到了节点对的数量。 | ||
|
||
本题中需要学到:当寻路的过程中需要考虑具体的路径时,使用**邻接表**可能比使用邻接矩阵是一个更好的选择。此外,使用邻接表之后只需要用深度优先或者广度优先搜索即可遍历全表全部路径,尤其是这种确定无环的图。 | ||
|
||
代码实现(java): | ||
|
||
```java | ||
class Solution { | ||
public int[] countPairsOfConnectableServers(int[][] edges, int signalSpeed) { | ||
//邻接表存储每个节点与直接相邻节点之间的距离 | ||
//对于每个节点(作为根节点),查询其从每个邻居分别出发,能够找到的speed整数倍节点,然后用乘法法则计算节点对数量 | ||
|
||
//1.首先构造邻接表 | ||
int n = edges.length + 1;//因为图是一个无向无环图,仅一个连通分量 | ||
List<int[]>[] adj_map = new ArrayList[n]; | ||
//最外层数组:每个节点;中层List:该节点的邻接节点们,长度不定所以是List;最内层数组存储邻接节点下标和距离 | ||
for(int i=0;i<n;i++){ | ||
adj_map[i] = new ArrayList<>(); | ||
} | ||
|
||
for(int[] edge:edges){ | ||
int x = edge[0]; | ||
int y = edge[1]; | ||
int dis = edge[2]; | ||
adj_map[x].add(new int[]{y, dis}); | ||
adj_map[y].add(new int[]{x, dis}); | ||
}//创建好了邻接表 | ||
|
||
//2.然后遍历所有的节点作为根节点,使其往每个方向走以查找可连接节点,不同方向的节点数相乘即为可连接数目 | ||
int ans[] = new int[n]; | ||
for(int i=0;i<n;i++){ | ||
int count=0; | ||
for(int[] node : adj_map[i]){ | ||
|
||
int temp_count = dfs(node[0], i, adj_map, node[1], signalSpeed); | ||
ans[i] += temp_count * count; | ||
count += temp_count; | ||
} | ||
} | ||
|
||
// System.out.println(dfs(1, 0, adj_map, 13, 1)); | ||
return ans; | ||
} | ||
|
||
private int dfs(int x, int from, List<int[]>[] adj_map, int dis_accu, int signalSpeed){ | ||
//dfs()返回从from节点开始,往x这个方向走,最终能找到几个可连接的节点,找到x时已经积累的距离是dis_accu | ||
//注意from和x一定是直接邻接的 | ||
//首先对于节点x本身,判断可连接性 | ||
int count = 0; | ||
if(dis_accu % signalSpeed == 0) count=1; | ||
//然后查找从x开始,除了from以外可连接的节点 | ||
for(int[] node : adj_map[x]){ | ||
if(node[0] != from){ | ||
int temp_dis_accu = dis_accu + node[1]; | ||
count += dfs(node[0], x, adj_map, temp_dis_accu, signalSpeed); | ||
} | ||
} | ||
return count; | ||
} | ||
} | ||
``` | ||
|
||
时间复杂度:$O(n^2)$,即以每个节点为根,遍历全表的时间消耗。 | ||
|
||
空间复杂度:$O(n)$,由于是仅一个连通分量的无向无环图,在存储邻接表的时候,路径总数量一定为`n-1`条,存储邻接表的空间即为最大的空间消耗。 |