LeetCode547. Friends Circles 利用union find | bfs | dfs三种方法解决

问题来源

此题来源于LeetCode547. Friend Circles,主要运用了并查集(union find)、广度优先遍历(bfs)和深度优先遍历(bfs)三种方法解决。

问题简述

给定一个 N×N 的矩阵 M 表示了N个人之见的朋友关系。如果 M[i][j]=1 那么 i j是直接朋友关系;如果 M[i][j]=1 M[j][k]=1 M[i][k]=0 ,那么 i k是间接朋友关系。我们假定有直接和间接朋友关系的人为一个朋友圈,试问 M 中有几个朋友圈。
比如:

输入: 
[[1,1,0],
 [1,1,0],
 [0,0,1]]
输出: 2
解释:第0和第1个人是直接朋友,他们构成1个朋友圈;第2个人自己构成一个朋友圈,所以返回2

又比如:

输入: 
[[1,1,0],
 [1,1,1],
 [0,1,1]]
输出: 1
解释:第0和第1个人是直接朋友,第1和第2个人是直接朋友,所以第0和第2个人是间接朋友,这3个人构成了1个朋友圈,所以返回1

值得注意的是:

  • 总是有M[i][j]=M[j][i]

    • 总是有 M[i][i]=1
    • 解决方案

      利用union find解决

      此处的并查集用到了路径压缩的优化。

      class Solution {
      private:
          vector<int> vec;
          int sz;
      
      private:
          void Initialize(int count){
              vec = vector<int>(count);
              sz = count;
              for (int i = 0; i < count; i++)
                  vec[i] = i;
          }
      
          int findRoot(int p){
              assert(p >= 0 && p < sz);
              while (vec[p] != p){
                  //路径压缩
                  vec[p] = vec[vec[p]];
                  p = vec[p];
              }
              return vec[p];
          }
      
          void unionNode(int p, int q){
              assert(p >= 0 && p < sz && q >= 0 && q < sz);
              int pRoot = findRoot(p);
              int qRoot = findRoot(q);
              if (pRoot != qRoot)
                  vec[pRoot] = qRoot;
          }
      
      public:
          int findCircleNum(vector<vector<int>>& M) {
              int m = M.size();
              if (0 >= m)
                  return 0;
              Initialize(m);
              for (int i = 0; i < m; i++){
                  for (int j = i + 1 ; j < m; j++){
                      if (M[i][j] == 1)
                          unionNode(i, j);
                  }
              }
              int res = 0;
              for (int i = 0; i < m; i++)
                  if (vec[i] == i)
                      res++;
              return res;
          }
      };

      利用bfs解决

      由于问题的特殊性,每次都只要把对角线上的元素放到队列当中即可。

      class Solution {
      private:
          int sz;
      
      private:
          void bfs(vector<vector<int>> &M, int x){
              queue<int> q;
              q.push(x);
              while(!q.empty()){
                  int newX = q.front();
                  q.pop();
                  M[newX][newX] = 0;
                  for (int i = 0; i < sz; i++){
                      if (1 == M[newX][i]){
                          M[newX][i] = 0;
                          M[i][newX] = 0;
                          if (1 == M[i][i])
                              q.push(i);
                      }
                  }
              }
              return;
          }
      
      public:
          int findCircleNum(vector<vector<int>>& M) {
              int m = M.size();
              if (0 >= m)
                  return 0;
              sz = m;
              int res = 0;
              for (int i = 0; i < m; i++){
                  if (1 == M[i][i]){
                      bfs(M,i);
                      res++;
                  }
              }       
              return res;
          }
      };

      利用dfs解决

      dfs与bfs的思路大同小异,但元素过多可能会造成栈溢出。

      class Solution {
      private:
          int sz;
      
      private:
          void dfs(vector<vector<int>> &M, int x, int y){
              if (0 == M[x][y])
                  return;
              M[x][y] = 0;
              for (int i = 0; i < sz; i++){
                  if (1 == M[x][i]){
                      M[x][i] = 0;
                      M[i][x] = 0;
                      dfs(M, i, i);
                  }
              }
              return;
          }
      
      public:
          int findCircleNum(vector<vector<int>>& M) {
              int m = M.size();
              if (0 >= m)
                  return 0;
              sz = m;
              int res = 0;
              for (int i = 0; i < m; i++){
                  if (1 == M[i][i]){
                      dfs(M,i,i);
                      res++;
                  }
              }  
              return res;
          }
      };

      结束语

      以上三种方法最快的是并查集(union find),bfs和dfs的速度差不多,三者也许都还有优化的余地~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七元权

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值