这题是求点到点之间距离的问题,要点是一直循环记录就可以,退出条件为检测到成环了或者走到尽头
由于每个节点 至多 有一条出边,因此变成了一条没有分叉的路,BFS或者DFS都是可以的
public int closestMeetingNode(int[] edges, int node1, int node2) {
int n = edges.length; int[] dis1 = new int[n], dis2 = new int[n]; Arrays.fill(dis1, -1); dis1[node1] = 0; Arrays.fill(dis2, -1); dis2[node2] = 0; getDis(edges, node1, dis1); getDis(edges, node2, dis2); int res = -1; int min = 100010; for (int i = 0; i < n; i++) { if (dis1[i] == -1 || dis2[i] == -1) continue; int curMax = Math.max(dis1[i], dis2[i]); if (curMax < min){ min = curMax; res = i; } } return res; }
private void getDis(int[] edges, int node, int[] dis) { int cur = node, d = 0; while (edges[cur] != -1) { cur = edges[cur]; d++; if (dis[cur] != -1) break; dis[cur] = d; } }
|
基环树定义可参考:【算法笔记】基环树
这题也是基环树,因为出边限定了一条,但是可能有多个环
DFS时间戳一般是 times[i] 的形式,times[i] 记录的是首次访问节点 i 的时间节点
通过时间戳我们可以很轻易地得知该轮之前已经访问过了哪些节点并计算出距离
同时得知之前轮已经访问过的点,是一个兼具标记访问、标记先后顺序、标记距离、前缀和等参数的利器
可以用来判断是否成环、判断节点的父子关系等
class Solution { public int longestCycle(int[] edges) {
int res = -1; int n = edges.length; int[] times = new int[n]; for (int i = 0, clock = 1; i < n; i++) { if (times[i] > 0) continue; int start = clock; for (int x = i; x != -1; x = edges[x]) { if (times[x] > 0) { if(times[x] >= start) res = Math.max(res, clock - times[x]); break; } times[x] = clock++; } } return res; } }
|
class Solution { int[] nums, xr, in, out; int clock = 0; List<Integer>[] list;
public int minimumScore(int[] _nums, int[][] edges) {
nums = _nums; int res = 0x3f3f3f3f, n = nums.length; xr = new int[n]; in = new int[n]; out = new int[n]; list = new ArrayList[n]; for (int i = 0; i < n; i++) { list[i] = new ArrayList<>(); } for (int[] e : edges) { int a = e[0], b = e[1]; list[a].add(b); list[b].add(a); } dfs(0, -1); int a, b, c; for (int x = 1; x < n; x++) { for (int y = x + 1; y < n; y++) { if (isParent(x, y)) { a = xr[0] ^ xr[x]; b = xr[x] ^ xr[y]; c = xr[y]; } else if (isParent(y, x)) { a = xr[0] ^ xr[y]; b = xr[x]; c = xr[y] ^ xr[x]; } else { a = xr[0] ^ xr[x] ^ xr[y]; b = xr[x]; c = xr[y]; } int max = Math.max(a, Math.max(b, c)), min = Math.min(a, Math.min(b, c)); res = Math.min(res, max - min); if (res == 0) return 0; } } return res; }
private boolean isParent(int x, int y) { return in[x] < in[y] && out[y] <= out[x]; }
private void dfs(int i, int fa) { xr[i] = nums[i]; in[i] = ++clock; for (int next : list[i]) { if (next != fa) { dfs(next, i); xr[i] ^= xr[next]; } } out[i] = clock; } }
|
拓扑排序检测环的应用:
拓扑排序可以将不在环中的节点先全部删除掉然后就会剩下只在环中的节点
可以统计删除的节点数目对比总节点的数目就可以得知是否存在环
环中的节点个数可以直接通过for循环遍历进行统计
public int longestCycle(int[] edges) {
int n = edges.length; boolean[] vis = new boolean[n]; Queue<Integer> que = new LinkedList<>(); int[] inDegree = new int[n]; for (int e : edges) { if (e != -1) inDegree[e]++; } for (int i = 0; i < n; i++) { if (inDegree[i] == 0) { que.add(i); vis[i] = true; } } while (!que.isEmpty()) { int poll = que.poll(); int next = edges[poll]; if (next != -1 && --inDegree[next] == 0) { que.add(next); vis[next] = true; } } int res = -1; for (int i = 0; i < n; i++) { if (vis[i]) continue; int cur = i, cycleNum = 0; while (!vis[edges[cur]]) { cur = edges[cur]; cycleNum++; vis[cur] = true; } res = Math.max(res, cycleNum); } return res; }
|
拓扑排序的题目通常是:已知一系列限制某两个节点先后顺序的条件,要求解最后满足这些条件的节点总体先后顺序。
这道题就是典型的拓扑排序问题,条件可以看做边,只要得知两个维度可以独立分析,那么这道题就可以拆解成两次拓扑排序。
class Solution { public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) {
List<Integer> seq1 = getSeq(k, rowConditions), seq2 = getSeq(k, colConditions); if (seq1.size() < k || seq2.size() < k) return new int[0][0]; int[][] res = new int[k][k]; int[][] map = new int[k + 1][2]; for (int i = 0; i < k; i++) { map[seq1.get(i)][0] = i; map[seq2.get(i)][1] = i; } for (int i = 1; i <= k; i++) { res[map[i][0]][map[i][1]] = i; } return res; }
private List<Integer> getSeq(int k, int[][] conditions) { int[] inDegree = new int[k + 1]; HashSet<Integer>[] list = new HashSet[k + 1]; for (int i = 1; i <= k; i++) { list[i] = new HashSet<>(); } for (int[] r : conditions) { int a = r[0], b = r[1]; if (list[a].add(b)) { inDegree[b]++; } } List<Integer> seq = new ArrayList<>(); Queue<Integer> que = new LinkedList<>(); for (int i = 1; i <= k; i++) { if (inDegree[i] == 0) { que.add(i); seq.add(i); } } while (!que.isEmpty()) { int poll = que.poll(); for (int ne : list[poll]) { if (--inDegree[ne] == 0) { que.add(ne); seq.add(ne); } } } return seq; } }
|