Leetcode 1782. 统计点对的数目
这两天实训比较忙,之后补
2023年08月24日 16:10:44 补
题目描述
给你一个无向图,无向图由整数 n ,表示图中节点的数目,和 edges 组成,其中 edges[i] = [ui, vi] 表示 ui 和 vi 之间有一条无向边。同时给你一个代表查询的整数数组 queries 。
第 j 个查询的答案是满足如下条件的点对 (a, b) 的数目:
a < b
cnt 是与 a 或者 b 相连的边的数目,且 cnt 严格大于 queries[j] 。
请你返回一个数组 answers ,其中 answers.length == queries.length 且 answers[j] 是第 j 个查询的答案。
请注意,图中可能会有 多重边 。
例子1
输入:n = 4, edges = [[1,2],[2,4],[1,3],[2,3],[2,1]], queries = [2,3]
输出:[6,5]
解释:每个点对中,与至少一个点相连的边的数目如上图所示。
answers[0] = 6。所有的点对(a, b)中边数和都大于2,故有6个;
answers[1] = 5。所有的点对(a, b)中除了(3,4)边数等于3,其它点对边数和都大于3,故有5个。
例子2
输入:n = 5, edges = [[1,5],[1,5],[3,4],[2,5],[1,3],[5,1],[2,3],[2,5]], queries = [1,2,3,4,5]
输出:[10,10,9,8,6]
代码
当然是大佬的代码了(哭)
class Solution {
public int[] countPairs(int n, int[][] edges, int[] queries) {
// deg[i] 表示与点 i 相连的边的数目
var deg = new int[n + 1]; // 节点编号从 1 到 n
var cntE = new HashMap<Integer, Integer>();
for (var e : edges) {
int x = e[0], y = e[1];
if (x > y) {
// 交换 x 和 y,因为 1-2 和 2-1 算同一条边
int tmp = x;
x = y;
y = tmp;
}
deg[x]++;
deg[y]++;
// 统计每条边的出现次数
// 用一个 int 存储两个不超过 65535 的数
cntE.merge(x << 16 | y, 1, Integer::sum); // cntE[x<<16|y]++
}
var ans = new int[queries.length];
var sortedDeg = deg.clone();
Arrays.sort(sortedDeg); // 排序,为了双指针
for (int j = 0; j < queries.length; j++) {
int q = queries[j];
int left = 1, right = n; // 相向双指针
while (left < right) {
if (sortedDeg[left] + sortedDeg[right] <= q) {
left++;
} else {
ans[j] += right - left;
right--;
}
}
for (var e : cntE.entrySet()) {
int k = e.getKey(), c = e.getValue();
int s = deg[k >> 16] + deg[k & 0xffff]; // 取出 k 的高 16 位和低 16 位
if (s > q && s - c <= q) {
ans[j]--;
}
}
}
return ans;
}
}
大佬优化过的代码
class Solution {
public int[] countPairs(int n, int[][] edges, int[] queries) {
var deg = new int[n + 1];
var cntE = new HashMap<Integer, Integer>();
for (var e : edges) {
int x = e[0], y = e[1];
if (x > y) {
int tmp = x;
x = y;
y = tmp;
}
deg[x]++;
deg[y]++;
cntE.merge(x << 16 | y, 1, Integer::sum);
}
// 统计 deg 中元素的出现次数
var cntDeg = new HashMap<Integer, Integer>();
int maxDeg = 0;
for (int i = 1; i <= n; i++) {
cntDeg.merge(deg[i], 1, Integer::sum); // cntDeg[deg[i]]++
maxDeg = Math.max(maxDeg, deg[i]);
}
// 2)
var cnts = new int[maxDeg * 2 + 2];
for (var e1 : cntDeg.entrySet()) {
int deg1 = e1.getKey(), c1 = e1.getValue();
for (var e2 : cntDeg.entrySet()) {
int deg2 = e2.getKey(), c2 = e2.getValue();
if (deg1 < deg2)
cnts[deg1 + deg2] += c1 * c2;
else if (deg1 == deg2)
cnts[deg1 + deg2] += c1 * (c1 - 1) / 2;
}
}
// 3)
for (var e : cntE.entrySet()) {
int k = e.getKey(), c = e.getValue();
int s = deg[k >> 16] + deg[k & 0xffff];
cnts[s]--;
cnts[s - c]++;
}
// 4) 计算 cnts 的后缀和
for (int i = cnts.length - 1; i > 0; i--)
cnts[i - 1] += cnts[i];
for (int i = 0; i < queries.length; i++)
queries[i] = cnts[Math.min(queries[i] + 1, cnts.length - 1)];
return queries;
}
}
我的代码
//哈哈哈,这次我就想到一个先遍历找,然后想有关图的东西,然后脑子空空
思路
大佬思路
没错,在上面的链接里
我滴思路
遍历,双指针,啊。。。。啥也不会了,图我还没学明白,一会儿去恶补,再来补!!
分类:
leetcode题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)