leetcode
leetcode 1551 ~ 1600
一个图中连通三元组的最小度数

一个图中连通三元组的最小度数

难度:

标签:

题目描述

给你一个无向图,整数 n 表示图中节点的数目,edges 数组表示图中的边,其中 edges[i] = [ui, vi] ,表示 ui 和 vi 之间有一条无向边。

一个 连通三元组 指的是 三个 节点组成的集合且这三个点之间 两两 有边。

连通三元组的度数 是所有满足此条件的边的数目:一个顶点在这个三元组内,而另一个顶点不在这个三元组内。

请你返回所有连通三元组中度数的 最小值 ,如果图中没有连通三元组,那么返回 -1 。

 

示例 1:

输入:n = 6, edges = [[1,2],[1,3],[3,2],[4,1],[5,2],[3,6]]
输出:3
解释:只有一个三元组 [1,2,3] 。构成度数的边在上图中已被加粗。

示例 2:

输入:n = 7, edges = [[1,3],[4,1],[4,3],[2,5],[5,6],[6,7],[7,5],[2,6]]
输出:0
解释:有 3 个三元组:
1) [1,4,3],度数为 0 。
2) [2,5,6],度数为 2 。
3) [5,6,7],度数为 2 。

 

提示:

  • 2 <= n <= 400
  • edges[i].length == 2
  • 1 <= edges.length <= n * (n-1) / 2
  • 1 <= ui, vi <= n
  • ui != vi
  • 图中没有重复的边。

代码结果

运行时间: 88 ms, 内存: 30.6 MB


/*
 * 思路:
 * 1. 使用邻接矩阵表示图,graph[i][j] == true 表示节点 i 和节点 j 之间有一条边。
 * 2. 枚举每一个可能的三元组 (i, j, k),检查它们是否两两之间有边。
 * 3. 对于每个有效的三元组,计算其度数。
 * 4. 返回所有三元组的度数的最小值。
 */

import java.util.*;
import java.util.stream.IntStream;

public class Solution {
    public int minTrioDegree(int n, int[][] edges) {
        boolean[][] graph = new boolean[n + 1][n + 1];
        int[] degree = new int[n + 1];
        Arrays.stream(edges).forEach(edge -> {
            graph[edge[0]][edge[1]] = true;
            graph[edge[1]][edge[0]] = true;
            degree[edge[0]]++;
            degree[edge[1]]++;
        });
        int minDegree = IntStream.range(1, n + 1)
            .flatMap(i -> IntStream.range(i + 1, n + 1)
                .filter(j -> graph[i][j])
                .flatMap(j -> IntStream.range(j + 1, n + 1)
                    .filter(k -> graph[i][k] && graph[j][k])
                    .map(k -> degree[i] + degree[j] + degree[k] - 6)))
            .min()
            .orElse(Integer.MAX_VALUE);
        return minDegree == Integer.MAX_VALUE ? -1 : minDegree;
    }
}

解释

方法:

此题解采用的是一种基于邻接表的方法来查找图中所有的连通三元组,并计算它们的度数。首先,使用一个哈希表 `maps` 来存储每个节点的邻接节点。对于每个节点 `i`,如果它的邻接节点少于两个,则无法形成三元组,跳过这样的节点。对于其他节点,首先对它们的邻接节点按照邻接节点数进行排序,以便优先处理可能形成连通三元组的节点组合。使用一种类似于广度优先搜索的方法来穷举所有可能的三元组组合,一旦找到一个有效的三元组,即计算其度数并更新最小度数。如果在遍历过程中发现存在度数更小的三元组,则更新结果 `res`。最终,如果没有找到任何连通三元组,返回 -1,否则返回最小的度数。

时间复杂度:

O(n^3)

空间复杂度:

O(E + n^2)

代码细节讲解

🦆
如何保证从邻接表中找到的三元组实际上是连通的?即如何验证三个节点两两之间都有边连接?
在算法中,确保三个节点i, j, k形成连通三元组的依据是对每个节点i遍历其邻接节点列表arr,检查任意两个节点j和k(从arr中选择)是否存在于彼此的邻接列表中。具体地,对于每对j和k,我们检查j是否在节点k的邻接列表maps[k]中。只有当i, j和k两两直接相连,即i与j相连,i与k相连,且j与k相连时,它们才被视为一个有效的连通三元组。
🦆
在算法中使用的BFS搜索方法具体是如何实现的?题解中提到使用队列q和访问集合v,能否详细解释这部分的逻辑和作用?
虽然题解中提到使用类似BFS的方法,但实际的实现更类似于广度优先搜索的变体。这里使用队列q来存储待检查的节点对索引(l, r),这些索引对应于节点i的邻接节点arr中的元素。队列的初始状态是(0, 1),表示检查arr中的第一个和第二个元素。访问集合v用于记录已经访问过的节点对索引,以避免重复处理相同的节点对。对于队列中的每个元素(l, r),检查通过这些索引在arr中找到的节点j和k是否形成一个三元组。若不满足条件,将新的节点对索引加入队列,继续检查。
🦆
为什么在更新最小度数时,需要从各节点的度数总和中减去6?这里的6代表了什么具体的含义?
在计算三元组的度数时,我们首先计算三个节点i, j, k的度数之和。然而,因为每个边被计算了两次(每个节点对其邻接节点的连接),所以i, j, k之间的三条内部边也被重复计算了。这导致总度数被多计算了6(每条边计算两次,共3条边)。因此,我们从总和中减去6,以得到实际的外部连通度数,即除去三元组内部的连接之外,各节点还与外部有多少连接。
🦆
题解中提到如果节点的邻接节点少于两个就跳过该节点,这个判断的依据是什么?
一个节点要形成三元组,至少需要与两个其他节点连通。如果一个节点的邻接节点少于两个,则意味着它不可能与两个其他节点形成完全连接的三元组。因此,对于那些邻接节点少于两个的节点,我们可以直接跳过,因为它们无法构成所需的连通三元组。这是一个基于图的结构性质的有效优化,避免了不必要的计算。

相关问题