② 实现 Insert 时报错 “The executor will produce a single tuple of integer type as the output, indicating how many rows have been inserted into the table”,并且可以看到 Next 函数的注释中表示 “tuple The integer tuple indicating the number of rows inserted into the table”。说实话有点难以理解,我一开始以为每次调用 Next 会像迭代器模式一样,只执行一次插入,但是这样实现就会报上面的错误。然后通过查看 Discord 的讨论,发现是一次性插入所有记录,因为只要返回 true 就会打印插入的行数,返回 false 就不会打印。当插入零行时,还必须打印一个零,这说明,Next 必定要先返回 true,再返回 false。并且在构造 tuple 时需要使用 BIGINT 类型,不然会报其他错误(明明注释说的是 INTEGER 额)。
① 一开始实现真摸不着头脑,AggregationPlanNode 里面怎么这么多东西。group_bys 是指 GROUP BY 时对列的求值表达式,aggregates 是指使用聚合函数时对列的求值表达式,agg_types 是指聚合函数的类型。例如:GROUP BY DAY(col)、MIN(col1 + col2)。我们使用 InsertCombine 函数向哈希表插入值,参数可以使用 MakeAggregateKey 和 MakeAggregateValue 函数获得。
② 根据项目介绍,AggregationExecutor::Next 返回的 tuple 应该包含 key 和 value(我没看到,找错好难)。特别需要注意,当哈希表为空时,应该返回什么:如果是对某列进行 GROUP BY 操作,那么就返回 false,因为有个测试用例有注释 no groups, no output;否则,返回 true,并且 tuple 存储聚合函数的默认值。(可以通过判断 key 模式的列数是否为零,或者 value 模式的列数是否等于 plan_ 输出模式的列数,来判断当前是否对某列进行 GROUP BY 操作)
privatestaticint[] topologicalSort(int n, int[][] edges) { int[] in = newint[n]; List<Integer>[] g = newList[n]; Arrays.setAll(g, k -> newArrayList<>()); for (var e : edges) { intu= e[0], v = e[1]; g[u].add(v); in[v]++; }
Queue<Integer> q = newArrayDeque<>(); for (inti=0; i < n; i++) { if (in[i] == 0) { q.offer(i); } }
intidx=0; int[] res = newint[n]; while (!q.isEmpty()) { intx= q.poll(); res[idx++] = x; for (int y : g[x]) { if (--in[y] == 0) { q.offer(y); } } }
privatestaticintprim(int n, int[][] edges) { List<int[]>[] g = newList[n]; Arrays.setAll(g, k -> newArrayList<>()); for (var e : edges) { intu= e[0], v = e[1], w = e[2]; g[u].add(newint[]{v, w}); g[v].add(newint[]{u, w}); }
int[] d = newint[n]; Arrays.fill(d, Integer.MAX_VALUE); boolean[] vis = newboolean[n]; Queue<int[]> q = newPriorityQueue<>((a, b) -> a[1] - b[1]);
intres=0, cnt = 0; d[0] = 0; q.offer(newint[]{0, 0}); while (!q.isEmpty()) { intu= q.poll()[0]; if (vis[u]) continue; vis[u] = true; res += d[u]; if (++cnt == n) break; for (int[] t : g[u]) { intv= t[0], w = t[1]; if (!vis[v] && d[v] > w) { d[v] = w; q.offer(newint[]{v, d[v]}); } } }
// 不是连通图,最小生成树不存在 assert cnt == n;
return res; }
Kruskal
时间复杂度为 \(O(m\log{m})\)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
privatestaticintkruskal(int n, int[][] edges) { Arrays.sort(edges, (a, b) -> a[2] - b[2]);
intcnt=1, res = 0; UnionFinduf=newUnionFind(n); for (var e : edges) { intu= e[0], v = e[1], w = e[2]; if (uf.connected(u, v)) continue; uf.union(u, v); res += w; if (++cnt == n) break; }
privatestaticintdijkstra(int n, int[][] edges) { List<int[]>[] g = newList[n]; Arrays.setAll(g, k -> newArrayList<>()); for (var e : edges) { intu= e[0], v = e[1], w = e[2]; g[u].add(newint[]{v, w}); }
int[] d = newint[n]; Arrays.fill(d, INF); boolean[] vis = newboolean[n]; Queue<int[]> q = newPriorityQueue<>((a, b) -> a[1] - b[1]);
d[0] = 0; q.offer(newint[]{0, 0}); while (!q.isEmpty()) { intu= q.poll()[0]; if (u == n - 1) break; if (vis[u]) continue; vis[u] = true; for (int[] t : g[u]) { intv= t[0], w = t[1]; if (d[v] > d[u] + w) { d[v] = d[u] + w; q.offer(newint[]{v, d[v]}); } } }
return d[n - 1] == INF ? -1 : d[n - 1]; }
Bellman-Ford
时间复杂度为 \(O(nm)\)。
使用场景:解决任意边权的单源最短路问题;判断是否存在负环;解决有边数限制的单源最短路问题。
实现一:朴素版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
privatestaticfinalintINF= (int) 1e9;
privatestaticintbellmanFord(int n, int[][] edges) { int[] d = newint[n]; Arrays.fill(d, INF);
d[0] = 0; for (inti=0; i < n; i++) { for (var e : edges) { intu= e[0], v = e[1], w = e[2]; d[v] = Math.min(d[v], d[u] + w); } }
privatestaticintspfa(int n, int[][] edges) { List<int[]>[] g = newList[n]; Arrays.setAll(g, k -> newArrayList<>()); for (var e : edges) { intu= e[0], v = e[1], w = e[2]; g[u].add(newint[]{v, w}); }
int[] d = newint[n]; Arrays.fill(d, INF); Queue<Integer> q = newArrayDeque<>(); boolean[] on = newboolean[n];
d[0] = 0; q.offer(0); on[0] = true; while (!q.isEmpty()) { intu= q.poll(); on[u] = false; for (int[] t : g[u]) { intv= t[0], w = t[1]; if (d[v] > d[u] + w) { d[v] = d[u] + w; if (!on[v]) { q.offer(v); on[v] = true; } } } }