文章目录
- 1.计算出度+排序+哈希
- 2.枚举
- 3.贪心
- 4.思考
1615. 最大网络秩
 
 
在不考虑两座道路直接相连时,我们求出入度(或出度)最大的两个点即可。
若相连,则存在一条边,所以我们将边存入一个集合中,快速查找是否存在。
1.计算出度+排序+哈希
在使用哈希存边时,unordered_set不能存储pair<type1,type2>类型,set可以,但是set速度更慢。为了使用unordered_set,我们可以观察数据大小,注意到n≤100,因此我们可以将两个数合并成一个数:
- 对于一条边(a,b),我们确保a<b(这样不会重复存边),将a保存在高2位,b保存在低2位。如(15,2),处理过后就是1502。这是一种映射关系,并且是无序边集到整数的一一映射。
排序: 
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         l 
        
       
         o 
        
       
         g 
        
       
         n 
        
       
         ) 
        
       
      
        O(nlogn) 
       
      
    O(nlogn)
 求最大网络秩的最坏时间复杂度: 
     
      
       
       
         O 
        
       
         ( 
        
        
        
          n 
         
        
          2 
         
        
       
         ) 
        
       
      
        O(n^2) 
       
      
    O(n2),因为要考虑两个顶点相连的情况。
class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector< pair<int, int> > outDegrees(n);
        for(int i = 0; i < n; ++ i){
            outDegrees[i].second = i;
            outDegrees[i].first = 0;
        }
        unordered_set<int> st;
        for(auto & i : roads){
            outDegrees[i[0]].first ++;
            outDegrees[i[1]].first ++;
            int x = i[0], y = i[1];
            if(x > y) swap(x, y);
            st.insert(x * 100 + y);
        }
        sort(outDegrees.begin(),outDegrees.end());
        int mx = -1;
        for(int i = n - 1; i >= 1; -- i){
            int j = i - 1;
            for(; j >= 0; -- j){
                int tmp = outDegrees[i].first + outDegrees[j].first;
                int x = outDegrees[i].second;
                int y = outDegrees[j].second;
                if(x > y) swap(x, y);
                if(st.count(x * 100 + y)){
                    tmp -= 1;
                }
                if(tmp >= mx) mx = tmp;//只有严格小于才能跳过,等于时,后者可能更好,如4,4,4,前两个四有边,但第一个和第三个之间无边。
                else break;
            }
            if(j == i - 1) break;
        }
        return mx;
    }
};
- 虽然会比枚举快,但最坏也是 n 2 n^2 n2了,直接枚举
2.枚举
时间复杂度: O ( n 2 ) O(n^2) O(n2)
class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector<int> outDegrees(n, 0);
        unordered_set<int> st;
        for (const auto& road : roads) {
            outDegrees[road[0]]++;
            outDegrees[road[1]]++;
            int x = min(road[0], road[1]);
            int y = max(road[0], road[1]);
            st.insert(x * 100 + y);
        }
        int maxRank = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                int rank = outDegrees[i] + outDegrees[j];
                if (st.count(i * 100 + j)) {
                    rank--;
                }
                maxRank = max(maxRank, rank);
            }
        }
        return maxRank;
    }
};
官方:
 使用领接矩阵存储是否存在边,而没使用哈希。
class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector<vector<bool>> connect(n, vector<bool>(n, false));
        vector<int> degree(n, 0);
        for (auto v : roads) {
            connect[v[0]][v[1]] = true;
            connect[v[1]][v[0]] = true;
            degree[v[0]]++;
            degree[v[1]]++;
        }
        int maxRank = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int rank = degree[i] + degree[j] - (connect[i][j] ? 1 : 0);
                maxRank = max(maxRank, rank);
            }
        }
        return maxRank;
    }
};
3.贪心
我们考虑到方法一,我们使用了排序这个思想,使得从最大的选取,但是我们在之后发现了,即使是排序了我们也不得不可能使得内层循环是 O ( n ) O(n) O(n)次。
我们是否能有更好的办法呢?我们可以观察到,我们取得的两个最大值,最小的时候它们的秩也是它们之和再-1。那么我们还有必要考虑比这两个最大值其中的一个更小的值吗?没有了!因为更小的时候最大也是其中一个-1,这和只考虑他俩是没有区别的!
有了这样一个想法,我们可以更快速的解决这个问题:
- 当出度的最大值有多个时,答案必然在这个最大值中选择两个产生,不管是否它们有连接。
- 当出度的最大值只有一个时,答案必然包含该最大值。
由于任何多次遍历都是 O ( n ) O(n) O(n),因此我们可以直接找到该最大值的集合。
class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector< int > outDegrees(n);
        unordered_set<int> st;
        for(auto & i : roads){
            outDegrees[i[0]] ++;
            outDegrees[i[1]] ++;
            int x = i[0], y = i[1];
            if(x > y) swap(x, y);
            st.insert(x * 100 + y);
        }
        int mx = -1;//度的最大值
        for(auto & i : outDegrees){
            if(i > mx) mx = i;
        }
        vector<int> mxDegrees;//存储最大度的节点数
        for(int i = 0; i < n; ++ i){
            if(outDegrees[i] == mx) mxDegrees.emplace_back(i);
        }
        int ans = -1;
        if(mxDegrees.size() == 1){//最大度就一个
            for(int i = 0; i < n; ++ i){
                if(outDegrees[i] != mx){
                    int x = mxDegrees.back();
                    int y = i;
                    if(x > y) swap(x, y);
                    ans = max(outDegrees[i] + mx - (st.count(x * 100 + y) == 0 ? 0 : 1), ans);
                }
            }
        }else{
        	int m = roads.size();
            if (mxDegrees.size() * (mxDegrees.size() - 1) / 2 > m) {
                return mx * 2;
            }
            for(int i = 0; i < mxDegrees.size(); ++ i){
                for(int j = i + 1; j < mxDegrees.size(); ++ j){
                    ans = max(ans, mx * 2 - (st.count(mxDegrees[i] * 100 + mxDegrees[j]) == 0 ? 0 : 1));
                }
            }
        }
        return ans;
    }
};

4.思考

 这里将所有出度为最大值的顶点放入到 
     
      
       
       
         f 
        
       
         i 
        
       
         r 
        
       
         s 
        
       
         t 
        
       
         A 
        
       
         r 
        
       
         r 
        
       
      
        firstArr 
       
      
    firstArr集合中,然后考虑其定点数 
     
      
       
       
         x 
        
       
      
        x 
       
      
    x,与所有边的个数的关系。
实际上这里就是在考虑这样一个问题:
 如果一个顶点边集构成的图不是一个完全图,那么是否一定存在两个点之间没有边?
 答案是对的!
证明一下:
如果一个图是一个完全图,则必然这个图中的所有顶点之间存在两个点没有边相连。
如果我们将这个图的某条边删除,则这个图一定不是完全图,则删除的这条边的两个顶点没有边相连。
则我们可以知道如果一个图不是完全图,则必然可以通过它的完全图删除某些边得到,而每次删除的边都会使得删除的这条边的两个顶点没有边相连。
即一个图如果不是完全图,则必然存在两个点没有边相连。
所以题解中用到的优化就是,判断该图是否可能是完全图,如果是则没办法,如果不可能是则直接判定一定存在两个顶点没有边相连。
使用:
 
     
      
       
        
         
         
           x 
          
         
           ∗ 
          
         
           ( 
          
         
           x 
          
         
           − 
          
         
           1 
          
         
           ) 
          
         
        
          2 
         
        
       
         与边数 
        
       
         m 
        
       
         比较 
        
       
      
        \frac{x*(x-1)}{2} 与边数m比较 
       
      
    2x∗(x−1)与边数m比较,
  
     
      
       
        
         
         
           x 
          
         
           ∗ 
          
         
           ( 
          
         
           x 
          
         
           − 
          
         
           1 
          
         
           ) 
          
         
        
          2 
         
        
       
      
        \frac{x*(x-1)}{2} 
       
      
    2x∗(x−1)代表了图是一个完全图是边的个数,如果这个个数比边数m大,则必然这个图不是完全图,即一定存在两个点之间没有边,既然这个集合里面的出度都一样,那么我们只需选取这两个没有边的点就可以得到答案为 
     
      
       
       
         2 
        
       
         ∗ 
        
       
         m 
        
       
         x 
        
       
      
        2*mx 
       
      
    2∗mx。
我们注意到m是整个图中的边数,而不是 f i r s t A r r firstArr firstArr中的边数,原因在于求 f i r s t A r r firstArr firstArr中的边数比较复杂,直接使用m代替了。
当然这里思考并不是对这个题目而言的,这题目可能最坏还是 O ( n 2 ) O(n^2) O(n2),只需要思考出贪心,并理解为什么非完全图一定存在两个点之间没有边即可。
![[每周一更]-(第107期):经典面试题-从输入URL到页面加载发生了什么](https://i-blog.csdnimg.cn/direct/b4480d82852246c891dd0c4fb3aaacd8.jpeg#pic_center)


















