547. Friend Circles(python+cpp)

题目:

here are N students in a class. Some of them are friends, while some are not. Their friendship is transitive in nature. For example, if A is a direct friend of B, and B is a direct friend of C, then A is an indirect friend of C. And we defined a friend circle is a group of students who are direct or indirect friends.
Given a N*N matrix M representing the friend relationship between students in the class. If M[i][j] = 1, then the ith and jth students are direct friends with each other, otherwise not. And you have to output the total number of friend circles among all the students.
Example 1:

Input:   
      [[1,1,0],  
       [1,1,0],  
       [0,0,1]] 
Output: 2
Explanation:The 0th and 1st students are direct friends, so they are in a friend circle.  
The 2nd student himself is in a friend circle. Soreturn 2. 

Example 2:

Input:  
     [[1,1,0],  
      [1,1,1],  
      [0,1,1]] 
Output: 1
Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are 
direct friends,  so the 0th and 2nd students are indirect friends. All of them are in the
same friend circle, so return 1. 

Note:
N is in range [1,200].
M[i][i] = 1 for all students.
If M[i][j] = 1, then M[j][i] = 1.

解释:
这道题可以用dfs做,也可以用并查集做,dfs的速度居然比并查集快是怎么回事?
dfs解法,遍历每一个结点,遍历到这个结点的时候,会把它所有的直接朋友和间接朋友都遍历到,而且会将其加入到visited中,所以如果遍历到的一个结点不在visited中,则证明它不在之前的朋友圈中,可以开启一个新的朋友圈了,在对其进行dfs。
python代码:


class Solution(object):
    def findCircleNum(self, M):
        """
        :type M: List[List[int]]
        :rtype: int
        """
        _set=set()
        
        def dfs(node):
            for i,val in enumerate(M[node]):
                if val and i not in _set:
                    _set.add(i)
                    dfs(i)
        result=0
        for i in range(len(M)):
            if i not in _set:
                result+=1
                dfs(i)
        return result

c++代码:

#include <set>
using namespace std;
class Solution {
public:
    set<int>visited;
    int findCircleNum(vector<vector<int>>& M) {
        int result=0;
        for(int i=0;i<M.size();i++)
        {
            if (find(visited.begin(),visited.end(),i)==visited.end())
            {
                result++;
                dfs(M,i);
            }
        }
        return result;
    }
    void dfs(vector<vector<int>> &M,int node)
    {
        for(int i=0;i<M[node].size();i++)
        {
            if (M[node][i] && find(visited.begin(),visited.end(),i)==visited.end())
            {
                visited.insert(i);
                dfs(M,i);
            }   
        } 
    }
};

并查集:《计算机考研机试指南》里面有一个用并查集求最大的朋友圈的大小的题目,可以参考。首先先把题目所给的矩阵转换成两两连线的形式,也就是如果两个人是朋友,则用点对(a,b)存起来并存入l数组edges中,因为一般的并查集就是这种输入。优化:其实不存edges也可以,在遍历的过程中完成并查集也可以。
优化前:

class Solution:
    def findCircleNum(self, M):
        """
        :type M: List[List[int]]
        :rtype: int
        """
        if not M:
            return 0
        if M[0].count(1)==len(M):
            return 1
        edges=[]
        for i in range(len(M)):
            for j in range(i,len(M[0])):
                if M[i][j]==1:
                    edges.append([i,j])
        Tree=[-1]*len(M)
        def findRoot(x):
            if Tree[x]==-1:
                return x
            else:
                temp=findRoot(Tree[x])
                Tree[x]=temp
                return temp
        for edge in edges:
            a,b=edge[0],edge[1]
            a=findRoot(a)
            b=findRoot(b)
            #若两个顶点不在一个集合中,则合并
            if a!=b:
                Tree[a]=b
        return Tree.count(-1)

优化后:

class Solution:
    def findCircleNum(self, M):
        """
        :type M: List[List[int]]
        :rtype: int
        """
        lenM=len(M)
        if M[0].count(1)==lenM:
            return 1
        Tree=[-1]*lenM
        def findRoot(x):
            if Tree[x]==-1:
                return x
            else:
                Tree[x]=findRoot(Tree[x])
                return Tree[x]
        for i in range(lenM):
            for j in range(i,lenM):
                if M[i][j]==1:
                    a,b=i,j
                    a=findRoot(a)
                    b=findRoot(b)
                    #若两个顶点不在一个集合中,则合并
                    if a!=b:
                        Tree[a]=b         
        return Tree.count(-1)

c++代码:

class Solution {
public:
    vector<int>Tree;
    int findCircleNum(vector<vector<int>>& M) {
        int lenM=M.size();
        Tree=vector<int>(lenM,-1);
        for (int i=0;i<lenM;i++)
        {
            for(int j=i;j<lenM;j++)
            {
                if(M[i][j]==1)
                {
                    int a=findRoot(i);
                    int b=findRoot(j);
                    if(a!=b)
                    {
                        Tree[a]=b;
                    }
                }
            }
        }
        return count(Tree.begin(),Tree.end(),-1);
    }
    int findRoot(int x)
    {
        if (Tree[x]==-1)
            return x;
        else
        {
            Tree[x]=findRoot(Tree[x]);
            return Tree[x];
        }
    }
};

总结:
再用并查集解法的时候,不一定非要先存成edges,可以在遍历的时候直接并查集,这样更节省时间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值