zoukankan      html  css  js  c++  java
  • 【互联网面试】朋友圈问题

    王道面试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 ", 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 ", sum);
                }
                return 0;
            }

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

  • 相关阅读:
    【原创】μC/OS Ⅱ工程中MAKETEST.BAT文件的改进与优化
    网络号和主机号等的分析和计算
    QQ电脑管家加速升级版便携版
    ARM发展历史
    μC/OS Ⅱ之任务就绪表的操作_2012.5.31
    删除word中的多余空行
    μC/OS II安装调试指南(带图解说明)
    WPF简单的User Control
    How to Databind SelectedItems of the ListPicker and RecurringDaysPicker
    数组排序
  • 原文地址:https://www.cnblogs.com/zhizhan/p/4868120.html
Copyright © 2011-2022 走看看