1615.最大网络秩

前言

题目来自LeetCode:https://leetcode.cn/problems/maximal-network-rank/

题目(难度:中等)

n 座城市和一些连接这些城市的道路 roads 共同组成一个基础设施网络。每个 roads[i] = [ai, bi] 都表示在城市 ai 和 bi 之间有一条双向道路。
两座不同城市构成的 城市对网络秩 定义为:与这两座城市 直接 相连的道路总数。如果存在一条道路直接连接这两座城市,则这条道路只计算 一次
整个基础设施网络的 最大网络秩 是所有不同城市对中的 最大网络秩
给你整数 n 和数组 roads,返回整个基础设施网络的 最大网络秩
示例1
示例1

1
2
3
输入:n = 4, roads = [[0,1],[0,3],[1,2],[1,3]]
输出:4
解释:城市 01 的网络秩是 4,因为共有 4 条道路与城市 01 相连。位于 01 之间的道路只计算一次。

提示:

  • 2 <= n <= 100
  • 0 <= roads.length <= n * (n - 1) / 2
  • roads[i].length == 2
  • 0 <= ai, bi <= n-1
  • ai != bi
  • 每对城市之间 最多只有一条 道路相连

题解

本题考的是图的度(degree),只要找到度最大和次大的结点,即为答案。因为同一条边只算一次,如果两结点之间有边,则需要减去1。由于本题2 <= n <= 100, 直接遍历节点对即可。若n的值大于达到10^5时,即O(n^2)超出时间限制时,那就需要用贪心方法在O(n)的时间复杂度内找出度最大和次大的两个结点。

代码

遍历结点对O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public int maximalNetworkRank(int n, int[][] roads) {
//邻接表
HashMap<Integer, HashSet<Integer>> map = new HashMap<>();
//初始化邻接表
for(int i = 0; i < n; i++){
HashSet<Integer> t = new HashSet<>();
map.put(i, t);
}
//将E(u,v)写入邻接表
for(int i = 0; i < roads.length; i++){
int u = roads[i][0];
int v = roads[i][1];
map.get(u).add(v);
map.get(v).add(u);
}

//遍历结点对
int max = -1;
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
//算出两结点的度和
int cul = map.get(i).size() + map.get(j).size();
//判断两结点是否有边
if(map.get(i).contains(j)){
cul--;
}
max = max > cul ? max : cul;
}
}
return max;
}
}

贪心O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class Solution {
public int maximalNetworkRank(int n, int[][] roads) {
HashMap<Integer, HashSet<Integer>> map = new HashMap<>();
for(int i = 0; i < n; i++){
HashSet<Integer> t = new HashSet<>();
map.put(i, t);
}

for(int i = 0; i < roads.length; i++){
int u = roads[i][0];
int v = roads[i][1];
map.get(u).add(v);
map.get(v).add(u);
}

int max = -1; //最大数
int smax = -1; //次大数
List<Integer> maxArr = new ArrayList<>(); //最大数结点集合
List<Integer> smaxArr = new ArrayList<>(); //次大数结点集合
for(int i = 0; i < n; i++){
int cur = map.get(i).size(); //当前结点度数
/*
四种情况
1 当前值大于最大值
2 当前值等于最大值
3 当前值小于最大值 大于次大值
4 当前值等于次大值
*/
if(cur > max){
smax = max;
smaxArr = new ArrayList<>(maxArr);
max = cur;
maxArr.clear();
maxArr.add(i);
}else if(cur == max){
maxArr.add(i);
}else if(cur > smax){
smaxArr.clear();
smaxArr.add(i);
smax = cur;
}else if(cur == smax){
smaxArr.add(i);
}
}

//当最大值队列只有1个元素,那么答案肯定在最大值结点和次大值结点之间,只需判断结点之间是否存在边
//当最大值队列不止一个元素,那么答案只会在最大值队列里,因为最大值结点中就算有边存在也是 2 * max - 1,
//而最大值结点和次大值结点最大也只是 max+(max-1)=2*max-1(最大值与次大值相差1,且两结点之间没有边),
//所以当最大值队列大于1时,可以不用考虑次大值队列
if(maxArr.size() == 1){
int first = maxArr.get(0);
for(int i : smaxArr){
if(!map.get(first).contains(i)){
return max + smax;
}
}
return max + smax - 1;
}else{
int size = maxArr.size();
// 如果所有边不能使最大值队列的结点互相有一条边,那么肯定存在有两个结点之间没有边
if(size * (size - 1) / 2 > roads.length){
return max * 2;
}
//判断最大值队列中是否有结点间存在边
for(int u : maxArr){
for(int v : maxArr){
if(u != v && !map.get(u).contains(v)){
return max * 2;
}
}
}
return max * 2 - 1;
}
}
}