【互联网面试】朋友圈问题

王道面试P231

问题描述:

假如已知有n个人和m对好友关系(存于数字r)。如果两个人是直接或间接的好友(好友的好友的好友...),

则认为他们属于同一个朋友圈,

请写程序求出这n个人里一共有多少个朋友圈。

假如:n = 5, m = 3, r = {{1 , 2} , {2 , 3} ,{4 , 5}},表示有5个人,1和2是好友,2和3是好友,4和5是好友,

则1、2、3属于一个朋友圈,4、5属于另一个朋友圈,结果为2个朋友圈。

最后请分析所写代码的时间、空间复杂度。评分会参考代码的正确性和效率。



小米2013年招聘笔试算法题--朋友圈

 

[cpp] view plaincopy
 
    1. //朋友圈问题  
    2.   
    3. #include <iostream>  
    4. using namespace std;  
    5.   
    6. int set[10001];  
    7. //带路径优化的并查集查找算法  
    8. inline int find(int x)               
    9. {    
    10.     int i,j,r;   
    11.     r = x;    
    12.     while(set[r] != r)     
    13.     {  
    14.         r = set[r];    
    15.     }  
    16.     i = x;  
    17.     while(i != r)  
    18.     {  
    19.         j = set[i];  
    20.         set[i] = r;  
    21.         i = j;  
    22.     }    
    23.     return r;    
    24. }   
    25. //优化的并查集归并算法  
    26. inline void merge(int x, int y)         
    27. {    
    28.     int t = find(x);    
    29.     int h = find(y);    
    30.     if(t < h)    
    31.     {  
    32.         set[h] = t;    
    33.     }  
    34.     else    
    35.     {  
    36.         set[t] = h;    
    37.     }  
    38. }  
    39.   
    40. int friends(int n , int m , int r[][2])    
    41. {    
    42.     int i , count;    
    43.     //初始化并查集,各点为孤立点,分支数为n  
    44.     for(i = 1 ; i <= n ; ++i)         
    45.     {  
    46.         set[i] = i;    
    47.     }  
    48.     for(i = 0 ; i < m ; ++i)    
    49.     {  
    50.         merge(r[i][0] , r[i][1]);    
    51.     }  
    52.     for(i = 1 ; i <= n ; ++i)         
    53.     {  
    54.         cout << set[i] <<" ";    
    55.     }  
    56.     count = 0;    
    57.     for(i = 1 ; i <= n ; ++i)    
    58.     {    
    59.         if(set[i] == i)    
    60.         {  
    61.             ++count;    
    62.         }  
    63.     }    
    64.     return count;    
    65. }  
    66.   
    67. void main()  
    68. {  
    69.     int n=5;  
    70.     int m=3;  
    71.     int a[][2]={{1,2},{2,3},{4,5}};  
    72.     cout << friends(n,m,a) <<endl;  
    73. }  
      1. http://blog.csdn.net/bxyill/article/details/8965530#comment
        1. 转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

          题目描述:假如已知有n个人和m对好友关系(存于数字r)。如果两个人是直接或间接的好友(好友的好友的好友...),则认为他们属于同一个朋友圈,请写程序求出这n个人里一共有多少个朋友圈。

          假如:n = 5 , m = 3 , r = {{1 , 2} , {2 , 3} , {4 , 5}},表示有5个人,1和2是好友,2和3是好友,4和5是好友,则1、2、3属于一个朋友圈,4、5属于另一个朋友圈,结果为2个朋友圈。 最后请分析所写代码的时间、空间复杂度。评分会参考代码的正确性和效率。

          显然本质就是求无向图的连通分量个数。而要求连通分量数,就是遍历图的过程。遍历完所有节点,需要调用遍历几次就是连通分量个数。比如题目中使用DFS,从节点1出发,可以遍历节点2,3,而要遍历完所有节点还需从节点4出发,再遍历一次,共遍历两次,因此连通分量数为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
          34
          35
          36
          37
          38
          39
          40
          41
          42
          43
          44
          45
          46
          47
          48
          49
          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          #define N 10000
          char map[N][N];
          char used[N];
          void dfs(int i, int n)
          {
              int j;
              used[i] = 1;
              for(j = 1; j <= n; j++) {
                  if (map[i][j] && !used[j])
                      dfs(j, n);
              }
          }
          /* 判断是否存在未访问节点
           * 若存在,则返回第一个未访问节点编号
           * 若不存在,则返回-1
           */
          int isVisitedAll(int n)
          {
              int i;
              for (i = 1; i <= n; i++)
                  if (used[i] == 0)
                      return i;
              return -1;
          }
          int main(int argc, char **argv)
          {
              int n, m;
              int a, b, i, sum, cur;
              while (scanf("%d%d", &n, &m) != EOF) {
                  if (n == 0)
                      break;
                  memset(map, 0, sizeof(map));
                  memset(used, 0, sizeof(used));
                  sum = 0;
                  for (i = 0; i < m; i++) {
                      scanf("%d%d", &a, &b);
                      map[a][b] = map[b][a] = 1;
                  }
                  while((cur = isVisitedAll(n)) != -1) {
                      sum++;
                      dfs(cur, n);
                  }
                  printf("%d\n", sum);
              }
              return 0;
          }

          暂且不说时间复杂度吧,空间复杂度就足够吓人了。首先需要一个表示图的01矩阵,大小为O(n  * n), 还需要记录是否节点是否已经被访问,需要大小为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
          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          #define N 100000
          int father[N];
          void init(int n)
          {
              int i;
              for (i = 1; i <= n; i++)
                  father[i] = i;
          }
          int getFather(int v)
          {
              if (father[v] == v)
                  return v;
              else {
           
                  father[v] = getFather(father[v]);
                  return father[v];
              }
          }
          void merge(int x, int y)
          {
              int fx = getFather(x);
              int fy = getFather(y);
              if (fx < fy)
                  father[fx] = fy;
              else
                  father[fy] = fx;
          }
          int same(int x, int y)
          {
              return getFather(x) == getFather(y);
          }
          int main(int argc, char **argv)
          {
              int n, m;
              int a, b;
              int i;
              int sum;
              while (scanf("%d%d", &n, &m) != EOF) {
           
                  if (n == 0)
                      break;
                  init(n);
                  sum = 0;
                  for (i = 1; i <= m; i++) {
                      scanf("%d%d", &a, &b);
                      merge(a, b);
                  }
                  for (i = 1; i <= n; i++) {
                      if (getFather(i) == i)
                          sum++;
                  }
                  printf("%d\n", sum);
              }
              return 0;
          }

          显然空间大大减少了,只需要O(n)的空间。

posted @ 2015-10-10 19:44  止战  阅读(388)  评论(0编辑  收藏  举报