//并查集
class UnionFind {
  public:
    vector<int> parent;//记录父节点
    int cnt;//连通分量
    UnionFind(int n) {
        this->cnt = n;
        this->parent.resize(n);
        for (int i = 0; i < n; ++i) {
            parent[i] = i;//初始父节点指向自己
        }

    }
    //找到x的根节点//根节点存在parent[x] = x,自己指向自身
    int Find(int x) {
        while (parent[x] != x) {
            x = parent[x];
        }
        return x;
    }
    //连接p和q
    void Union(int p, int q) {
        int rootp = Find(p);//返回p的根节点
        int rootq = Find(q);
        if (rootp == rootq)//如果两者根节点相同,则在同一个集合里面,跳过
            return ;
        //根节点不同,但是由于p,q为连接的,故将p的集合作为子节点连接在q下面:rootp->rootq
        parent[rootp] = rootq;
        cnt--;
    }
    //返回连通分量
    int Connect() {
        return cnt;
    }

};

class Solution {
  public:
    int citys(vector<vector<int> >& m) {
        // write code here
        int n = m.size();
        if (n == 1)
            return 1;
        UnionFind uf(n);
        //遍历邻接矩阵
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (i == j)
                    continue;
                if (m[i][j] == 1)
                    uf.Union(i, j);
            }
        }

        return uf.Connect();
    }
};

并查集的思想在于,最初初始化一个n的父节点为parent[n]并使其指向自身,然后通过外界条件不断吸纳与自身相连的元素成为一棵树,并使用parent[n]记录之间关系。

并查集查找自身的根节点比较方便。