统计点对的数目

92 阅读1分钟

1782. 统计点对的数目 - 力扣(LeetCode)

给你一个无向图,无向图由整数  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]
解释: 每个点对中,与至少一个点相连的边的数目如上图所示。

示例 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]

提示:

  • 2 <= n <= 2 * 10^4
  • 1 <= edges.length <= 10^5
  • 1 <= ui, vi <= n
  • ui != vi
  • 1 <= queries.length <= 20
  • 0 <= queries[j] < edges.length

思路

解本题我们先要求出无向图个节点的度degs,遍历各条边即可求出。查询的时候,我们双循环遍历各个顶点组,把他们的度求和,由于顶点组可能有边相连,如果这样我们会把一条边计算两次,还要减去顶点组之间的边数。这就是基本思路。由于点可能很多直接这样求解有可能超时,我们还要优化一下查询。
我们先把节点按度进行升序排序得到新的数组arr。拿到一个查询 q,我们定义两个指针 lr 分别指向arr的首尾,

  • arr[l] + arr[r] > q 时,arr[r]arr[l]arr[r]之间的节点组成节点组有可能符合查询,r 向前移继续遍历。
  • arr[l] + arr[r]<=q 时,l 向后移继续遍历。
  • l == r 时结束遍历

遍历结束后我们只是拿到了有可能符合查询的节点组,我们还好遍历边,把不符合条件的节点组移除。我们在最初遍历边的时候可以用 map 来缓存节点间边的条数,我们再次遍历边时,设 xy 分别为边的两个节点,当 degs[x] + degs[y] > q 并且 degs[x] + degs[y] - map[x][y]<=q 时,从备选节点组中移除。

解题

/**
 * @param {number} n
 * @param {number[][]} edges
 * @param {number[]} queries
 * @return {number[]}
 */
var countPairs = function (n, edges, queries) {
  const deg = new Array(n + 1).fill(0);
  const edgeMap = new Map();
  for (let edge of edges) {
    let x = Math.min(edge[0], edge[1]);
    let y = Math.max(edge[0], edge[1]);
    deg[x]++;
    deg[y]++;
    const key = (x << 16) | y;
    edgeMap.set(key, (edgeMap.get(key) || 0) + 1);
  }
  const arr = Array.from(deg).sort((a, b) => a - b);
  let res = Array(queries.length).fill(0);
  for (let i = 0; i < queries.length; i++) {
    const q = queries[i];
    let l = 1;
    let r = n;
    while (l < r) {
      if (arr[l] + arr[r] <= q) {
        l++;
      } else {
        res[i] += r - l;
        r--;
      }
    }
    for (const [k, v] of edgeMap.entries()) {
      const x = k >> 16;
      const y = k & 0xffff;
      if (deg[x] + deg[y] > q && deg[x] + deg[y] - v <= q) {
        res[i]--;
      }
    }
  }
  return res;
};