并查集

// 未改进版本
class Djset {
public:
    vector<int> f; // f记录每个人名的父辈是谁

    int findRoot(int x) {
        while (x != f[x]) {
            x = f[x];
        }
        return x;
    }
    void Union(int x, int y) {
        int x_root = findRoot(x);
        int y_root = findRoot(y);
        if (x_root != y_root) {
            f[x_root] = y_root;
        }
    }
    void Init(int n) {
        f = vector<int>(n);
        for (int i = 0; i < n; i++) {
            f[i] = i;
        }
    }
};
// 注意:使用该代码,并不能使得所有的元素都直接指向根节点,仍然存在间接的指向
class Djset {
public:
    vector<int> parent;  // 记录节点的根
    vector<int> rank;  // 记录根节点的深度(用于优化)
    Djset(int n): parent(vector<int>(n)), rank(vector<int>(n)) {
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }

    int find(int x) {
        // 压缩方式:直接指向根节点
        if (x != parent[x]) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    void merge(int x, int y) {
        int rootx = find(x);
        int rooty = find(y);
        if (rootx != rooty) {
            // 按秩合并
            if (rank[rootx] < rank[rooty]) {
                swap(rootx, rooty);
            }
            parent[rooty] = rootx;
            if (rank[rootx] == rank[rooty]) rank[rootx] += 1;
        }
    }
};

//leetcode 547 省份数量

class Solution {
    vector<int> f;
    int findRoot(int x) {
        while (x != f[x]) {
            x = f[x];
        }
        return x;
    }
    void merge(int x, int y) {
        int rootx = findRoot(x);
        int rooty = findRoot(y);
        if (rootx != rooty) {
            f[rootx] = rooty;
        }
    }
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        f = vector<int>(n);
        for (int i = 0; i < n; i++) {
            f[i] = i;
        }
        for (int i = 0; i < isConnected.size(); i++) {
            for (int j = i + 1; j < isConnected[i].size(); j++) {
                if (isConnected[i][j]) {
                    merge(i, j);
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < n; i++) {
            if(i == f[i]) {
                ans++;
            }
        }
        return ans;
    }
};

//684.冗余连接

class Solution {
    vector<int> f;
    int findRoot(int x) {
        while (x != f[x]) {
            x = f[x];
        }
        return x;
    }
    bool merge(int x, int y) {
        int rootx = findRoot(x);
        int rooty = findRoot(y);
        if (rootx != rooty) {
            f[rootx] = rooty;
            return true;
        } else {
            return false;
        }
    }
public:
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        int n = edges.size();
        f = vector<int>(n+1);
        for (int i = 1; i <= n; i++) {
            f[i] = i;
        }
        for (int i = 0; i < n; i++) {
            if (!merge(edges[i][0], edges[i][1])) {
                return edges[i];
            }
        }
        return vector<int>();
    }
};

//200.岛屿数量
/*

  • 给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
    岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
    此外,你可以假设该网格的四条边均被水包围。/
    class Union{
    int count;
    vector parent;
    public:
    Union(int n) {
    parent = vector(n);
    count = n;
    for (int i = 0; i < n; i++) {
    parent[i] = i;
    }
    }
    int findRoot(int x) {
    while (x != parent[x]) {
    // 压缩路径 优化
    parent[x] = parent[parent[x]];
    x = parent[x];
    }
    return x;
    }
    void merge(int x, int y) {
    if (findRoot(x) == findRoot(y)) {
    return;
    }
    parent[findRoot(x)] = findRoot(y);
    count–;
    }
    int GetCount() {
    return count;
    }
    };
    class Solution {
    int m, n;
    int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
    bool InArea(int x, int y) {
    return x >= 0 && x < m && y >= 0 && y < n;
    }
    public:
    int numIslands(vector<vector>& grid) {
    if (grid.size() == 0 || grid[0].size() == 0) {
    return 0;
    }
    m = grid.size();
    n = grid[0].size();
    Union uf(m
    n);
    int water = 0;
    for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
    if (grid[i][j] == ‘0’) {
    water++;
    } else {
    for (int k = 0; k < 4; k++) {
    int nx = i + dir[k][0];
    int ny = j + dir[k][1];
    if (InArea(nx, ny) && grid[nx][ny] == ‘1’) {
    uf.merge((in+j),(nxn+ny));
    }
    }
    }
    }
    }
    return uf.GetCount()-water;
    }
    };

//734句子相似性
/*
*给定两个句子 words1, words2 (每个用字符串数组表示),和一个相似单词对的列表 pairs ,判断是否两个句子是相似的。
例如,当相似单词对是 pairs = [[“great”, “fine”], [“acting”,“drama”], [“skills”,“talent”]]的时候,“great acting skills” 和 “fine drama talent” 是相似的。
注意相似关系是不具有传递性的。
例如,如果 “great” 和 “fine” 是相似的,“fine” 和 “good” 是相似的,但是 “great” 和 “good” 未必是相似的。
但是,相似关系是具有对称性的。
例如,“great” 和 “fine” 是相似的相当于 “fine” 和 “great” 是相似的。
而且,一个单词总是与其自身相似。
例如,句子 words1 = [“great”], words2 = [“great”], pairs = [] 是相似的,尽管没有输入特定的相似单词对。
最后,句子只会在具有相同单词个数的前提下才会相似。
所以一个句子 words1 = [“great”] 永远不可能和句子 words2 = [“doubleplus”,“good”] 相似。
*/

class Solution {
public:
bool areSentencesSimilar(vector& words1, vector& words2, vector<vector>& pairs) {
int sz1 = words1.size();
int sz2 = words2.size();
if (sz1 != sz2) {
return false;
}
unordered_map<string, unordered_set> m;
int n = pairs.size();
for (auto& p : pairs) {
m[p[0]].insert(p[1]);
m[p[1]].insert(p[0]);
}
for (int i = 0; i < sz1; i++) {
if (words1[i] == words2[i]) {
continue;
}
if (m[words1[i]].find(words2[i]) == m[words1[i]].end()
&& m[words2[i]].find(words1[i]) == m[words2[i]].end()) {
return false;
}
}
return true;
}
};

//737句子相似性II
/*

  • 给定两个句子 words1, words2 (每个用字符串数组表示),和一个相似单词对的列表 pairs ,判断是否两个句子是相似的。
    例如,当相似单词对是 pairs = [[“great”, “fine”], [“acting”,“drama”], [“skills”,“talent”]]的时候,words1 = [“great”, “acting”, “skills”] 和 words2 = [“fine”, “drama”, “talent”] 是相似的。
    注意相似关系是 具有 传递性的。
    例如,如果 “great” 和 “fine” 是相似的,“fine” 和 “good” 是相似的,则 “great” 和 “good” 是相似的。
    而且,相似关系是具有对称性的。
    例如,“great” 和 “fine” 是相似的相当于 “fine” 和 “great” 是相似的。
    并且,一个单词总是与其自身相似。
    例如,句子 words1 = [“great”], words2 = [“great”], pairs = [] 是相似的,尽管没有输入特定的相似单词对。
    最后,句子只会在具有相同单词个数的前提下才会相似。
    所以一个句子 words1 = [“great”] 永远不可能和句子 words2 = [“doubleplus”,“good”] 相似。/
    /
  • 并查集的核心是一个findRoot函数,如果两个元素属于同一群组的话,调用findRoot会返回相同的值。
  • 主要分为两部:1.建立群组关系,假设最开始每个元素都是独立的个体,各自属于不同的群组。
  • 对于每一个给定的关系对,对两个元素分别调用getRoot函数,找到两者的祖先节点,如果未建立过联系,那两者的祖先节点是不同的,此时要建立二者的关系。
  • 2.验证两个任意的元素是否属于同一群组,只要比较两者的祖先节点是否相同。
  • 保存群组关系的数据结构,针对输入的数据类型,有时用数组,有时用HashMap,如果输入元素是整型数的话,用数组即可,如果是字符串,需要用HashMap建立每一个结点和其祖先结点的映射*/
    class dsu
    {
    unordered_map<string,string> f;
    public:
    dsu(unordered_set &s)
    {
    for(auto& w : s)
    f[w] = w;//并查集初始化
    }
    void merge(string& a, string& b)
    {
    string fa = find(a);
    string fb = find(b);
    f[fa] = fb;
    }
    string find(string a)
    {
    string origin = a;
    while(a != f[a])
    a = f[a];
    return f[origin] = a;
    }
    };
    class Solution {
    public:
    bool areSentencesSimilarTwo(vector& words1, vector& words2, vector<vector>& pairs) {
    if(words1.size() != words2.size())
    return false;
    unordered_set s;
    for(auto& p : pairs)
    {
    s.insert(p[0]);
    s.insert(p[1]);
    }
    dsu u(s);//并查集
    for(auto& p : pairs)
    u.merge(p[0], p[1]);//merge
    for(int i = 0; i < words1.size(); ++i)
    {
    if(words1[i] == words2[i])
    continue;
    //并查集find
    if(u.find(words1[i]) != u.find(words2[i]))
    return false;
    }
    return true;
    }
    };

//1101 彼此熟识的最早时间
/*

  • 在一个社交圈子当中,有N个人。每个人都有一个从0到N-1唯一的id编号。
    我们有一份日志列表logs,其中每条记录都包含一个非负整数的时间戳,以及分属两个人的不同id,logs[i] = [timestamp, id_A, id_B]。
    每条日志标识出两个人成为好友的时间,友谊是相互的:如果A和B是好友,那么B和A也是好友。
    如果A是B的好友,或者A是B的好友的好友,那么就可以认为A也与B熟识。
    返回圈子里所有人之间都熟识的最早时间。如果找不到最早时间,就返回-1。
    示例:
    输入:logs = [[20190101,0,1],[20190104,3,4],[20190107,2,3],[20190211,1,5],[20190224,2,4],[20190301,0,3],[20190312,1,2],[20190322,4,5]], N = 6
    输出:20190301
    解释:
    第一次结交发生在 timestamp = 20190101,0 和 1 成为好友,社交朋友圈如下 [0,1], [2], [3], [4], [5]。
    第二次结交发生在 timestamp = 20190104,3 和 4 成为好友,社交朋友圈如下 [0,1], [2], [3,4], [5].
    第三次结交发生在 timestamp = 20190107,2 和 3 成为好友,社交朋友圈如下 [0,1], [2,3,4], [5].
    第四次结交发生在 timestamp = 20190211,1 和 5 成为好友,社交朋友圈如下 [0,1,5], [2,3,4].
    第五次结交发生在 timestamp = 20190224,2 和 4 已经是好友了。
    第六次结交发生在 timestamp = 20190301,0 和 3 成为好友,大家都互相熟识了。*/

class Solution {
int count;
vector f;
public:
int earliestAcq(vector <vector> &logs, int N) {
count = N;
f = vector(N);
for (int i = 0; i < N; i++) {
f[i] = i;
}
sort(logs.begin(), logs.end(), [](vector& a, vector& b) {
return a[0] < b[0];
});
for (auto& log : logs) {
merge(log[1], log[2]);
if (count == 1) {
return log[0];
}
}
return -1;
}
int findRoot(int x) {
while (x != f[x]) {
x = f[x];
}
return x;
}
void merge(int x, int y) {
int rootx = findRoot(x);
int rooty = findRoot(y);
if (rootx == rooty) {
return;
}
f[rootx] = rooty;
count–;
}
};

//1102.得分最高的路径
/*

  • 给你一个 R 行 C 列的整数矩阵 A。矩阵上的路径从 [0,0] 开始,在 [R-1,C-1] 结束。
    路径沿四个基本方向(上、下、左、右)展开,从一个已访问单元格移动到任一相邻的未访问单元格。
    路径的得分是该路径上的 最小 值。例如,路径 8 → 4 → 5 → 9 的值为 4 。
    找出所有路径中得分 最高 的那条路径,返回其 得分。/
    /
  • 把图里面的每一个点按照值的大小顺序,依次遍历,如果某个点的与其四联通的某个点已经被访问过,那么把这两个点所属的子图连接成一个图。
  • 一直遍历到左上角的位置和右下角的位置属于同一个图为止*/
    /*
  • Input: [[5,4,5],[1,2,6],[7,4,6]]
    Output: 4
    Explanation:
    The path with the maximum score is highlighted in yellow.
    */
    class Solution {
    public:
    int maximumMinimumPath(vector<vector>& A) {
    const int M = A.size();
    const int N = A[0].size();
    const int X = M * N;
    parent = vector(X, 0);
    for (int i = 0; i < parent.size(); ++i)
    parent[i] = i;
    vector<vector> values;
    for (int i = 0; i < M; ++i) {
    for (int j = 0; j < N; ++j) {
    values.push_back({A[i][j], i, j});
    }
    }
    sort(values.begin(), values.end(), [](vector& a, vector& b) {return a[0] < b[0];});
    unordered_set visited;
    visited.insert(0);
    visited.insert(X - 1);
    int res = min(A[0][0], A[M - 1][N - 1]);
    while(find(0) != find(X - 1)) {
    vector cur = values.back(); values.pop_back();
    visited.insert(cur[1] * N + cur[2]);
    res = min(res, cur[0]);
    for (auto& dir : dirs) {
    int newx = cur[1] + dir[0];
    int newy = cur[2] + dir[1];
    if (newx >= 0 && newx < M && newy >= 0 && newy < N && visited.count(newx * N + newy)) {
    uni(cur[1] * N + cur[2], newx * N + newy);
    }
    }
    }
    return res;
    }
    int find(int a) {
    if (parent[a] == a)
    return a;
    return find(parent[a]);
    }
    void uni(int a, int b) {
    int pa = find(a);
    int pb = find(b);
    if (pa == pb)
    return;
    parent[pa] = pb;
    }
    private:
    vector parent;
    vector<vector> dirs = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
    };

//1135最低成本联通所有城市
/*

  • 想象一下你是个城市基建规划者,地图上有 N 座城市,它们按以 1 到 N 的次序编号。
    给你一些可连接的选项 conections,其中每个选项 conections[i] = [city1, city2, cost] 表示将城市 city1 和城市 city2 连接所要的成本。
    (连接是双向的,也就是说城市 city1 和城市 city2 相连也同样意味着城市 city2 和城市 city1 相连)。
    返回使得每对城市间都存在将它们连接在一起的连通路径(可能长度为 1 的)最小成本。
    该最小成本应该是所用全部连接代价的综合。如果根据已知条件无法完成该项任务,则请你返回 -1。*/

//Kruskal:将边的权值排序,小的先遍历,用并查集检查两个顶点是否合并了,没有合并则将该边加入生成树
int count;
vector parent;
int findRoot(int x) {
while (x != parent[x]) {
x = parent[x];
}
return x;
}
void merge(int x, int y) {
int rootx = findRoot(x);
int rooty = findRoot(y);
if (rootx == rooty) {
return;
}
parent[rootx] = rooty;
count–;
}
int minimumCost(int N, vector<vector>& conections) {
count = N;
parent = vector(N+1);
for (int i = 1; i <= N; i++) {
parent[i] = i;
}
sort(conections.begin(), conections.end(), [](vector& a, vector& b) {
return a[2] < b[2];
});
int ans = 0;
for (const auto& conection : conections) {
int x = conection[0];
int y = conection[1];
int dis = conection[2];
if (findRoot(x) != findRoot(y)) {
merge(x, y);
ans += dis;
}
if (count == 1) {
return ans;
}
}
return -1;
}

//prim算法
/*

  • 把一个初始顶点的所有边加入优先队列
    取出最短的边,把这条边的另一个顶点相关的边加入队列
    再取出最小的边,重复下去,直到所有顶点加入过了*/
    struct cmp
    {
    bool operator()(const pair<int,int>& a, const pair<int,int>& b) const
    {
    return a.second > b.second;//小顶堆, 距离小的优先
    }
    };
    class Solution {
    public:
    int minimumCost(int N, vector<vector>& connections) {
    vector vis(N+1, false);
    vector<vector<pair<int,int>>> edges(N+1,vector<pair<int,int>>());
    for(auto& c : connections)
    {
    edges[c[0]].push_back({c[1],c[2]});
    edges[c[1]].push_back({c[0],c[2]});
    }
    priority_queue<pair<int,int>, vector<pair<int,int>>, cmp> q;
    int to, distance, total = 0, edge = 0;
    vis[1] = true;
    for(auto& e : edges[1])
    q.push(e);
    while(!q.empty())
    {
    to = q.top().first;
    distance = q.top().second;
    q.pop();
    if(!vis[to])
    {
    vis[to] = true;
    total += distance;
    edge++;
    if(edge == N-1)
    return total;
    for(auto& e : edges[to])
    q.push(e);
    }
    }
    return -1;
    }
    };

//261.以图辨树
/*

  • 给定从 0 到 n-1 标号的 n 个结点,和一个无向边列表(每条边以结点对来表示),请编写一个函数用来判断这些边是否能够形成一个合法有效的树结构。
    示例 1:
    输入: n = 5, 边列表 edges = [[0,1], [0,2], [0,3], [1,4]]
    输出: true

示例 2:
输入: n = 5, 边列表 edges = [[0,1], [1,2], [2,3], [1,3], [1,4]]
输出: false
注意:你可以假定边列表 edges 中不会出现重复的边。由于所有的边是无向边,边 [0,1] 和边 [1,0] 是相同的,因此不会同时出现在边列表 edges 中。*/
class dsu
{
vector f;
public:
dsu(int n)
{
f.resize(n);
for(int i = 0; i < n; ++i)
f[i] = i;
}
void merge(int a, int b)
{
int fa = find(a), fb = find(b);
f[fa] = fb;
}
int find(int a)
{
int origin = a;
while(a != f[a])
a = f[a];
return f[origin] = a;
}
int countUni()
{
int count = 0;
for(int i = 0; i < f.size(); ++i)
if(i == find(i))
count++;
return count;
}
};
class Solution {
public:
bool validTree(int n, vector<vector>& edges) {
dsu u(n);
for(auto& e : edges)
u.merge(e[0], e[1]);
return edges.size()+1==n && u.countUni()==1;
}
};

//1061.按字典序排列最小的等效字符串(1061_会员)
/*

  • 给出长度相同的两个字符串:A 和 B,其中 A[i] 和 B[i] 是一组等价字符。
    举个例子,如果 A = “abc” 且 B = “cde”,那么就有 ‘a’ == ‘c’, ‘b’ == ‘d’, ‘c’ == ‘e’。

等价字符遵循任何等价关系的一般规则:

自反性:‘a’ == ‘a’
对称性:‘a’ == ‘b’ 则必定有 ‘b’ == ‘a’
传递性:‘a’ == ‘b’ 且 ‘b’ == ‘c’ 就表明 ‘a’ == ‘c’
例如,A 和 B 的等价信息和之前的例子一样,
那么 S = “eed”, “acd” 或 “aab”,这三个字符串都是等价的,
而 “aab” 是 S 的按字典序最小的等价字符串
示例 1:
输入:A = “parker”, B = “morris”, S = “parser”
输出:“makkek”
解释:根据 A 和 B 中的等价信息,
我们可以将这些字符分为 [m,p], [a,o], [k,r,s], [e,i] 共 4 组。
每组中的字符都是等价的,并按字典序排列。所以答案是 “makkek”。*/
class dsu
{
vector f;
public:
dsu(int n)
{
f.resize(n);
for(int i = 0; i < n; ++i)
f[i] = i;
}
void merge(int a, int b)
{
int fa = find(a), fb = find(b);
if(fa > fb)//字母小的当代表,关键点
f[fa] = fb;
else
f[fb] = fa;
}
int find(int a)
{
int origin = a;
while(a != f[a])
a = f[a];
return f[origin] = a;
}
};
class Solution {
public:
string smallestEquivalentString(string A, string B, string S) {
dsu u(26);
for(int i = 0; i < A.size(); ++i)
u.merge(A[i]-‘a’, B[i]-‘a’);
for(int i = 0; i < S.size(); ++i)
S[i] = u.find(S[i]-‘a’)+‘a’;
return S;
}
};

//323.无向图中连通分量的数目(323_会员)
/*

  • 给定编号从 0 到 n-1 的 n 个节点和一个无向边列表(每条边都是一对节点),请编写一个函数来计算无向图中连通分量的数目。
    示例 1:
    输入: n = 5 和 edges = [[0, 1], [1, 2], [3, 4]]*/
    class Solution {
    public:
    int countComponents(int n, vector<vector>& edges) {
    map_ = vector(n, 0);
    components = n;
    for (int i = 0; i < n; ++i) {
    map_[i] = i;
    }
    for (vector& edge : edges) {
    uni(edge[0], edge[1]);
    }
    return components;
    }
    int find(int a) {
    if (a == map_[a])
    return a;
    return find(map_[a]);
    }
    void uni(int a, int b) {
    int pa = find(a);
    int pb = find(b);
    if (pa == pb)
    return;
    map_[pa] = pb;
    components --;
    }
    private:
    vector map_;
    int components;
    };

//924.尽量减少恶意软件的传播
/*

  • 在节点网络中,只有当 graph[i][j] = 1 时,每个节点 i 能够直接连接到另一个节点 j。
    一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件感染。这种恶意软件的传播将继续,直到没有更多的节点可以被这种方式感染。
    假设 M(initial) 是在恶意软件停止传播之后,整个网络中感染恶意软件的最终节点数。
    我们可以从初始列表中删除一个节点。如果移除这一节点将最小化 M(initial), 则返回该节点。如果有多个节点满足条件,就返回索引最小的节点。
    请注意,如果某个节点已从受感染节点的列表 initial 中删除,它以后可能仍然因恶意软件传播而受到感染。
    示例 1:
    输入:graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
    输出:0

示例 2:
输入:graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2]
输出:0

示例 3:
输入:graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1,2]
输出:1
注:将initial视为临时圈长,那么所拥有最大个数的圈,就是目标解。*/
class Solution{
private:
int father[305];
int node_counter[305];
int source_counter[305];
int find(int p)
{
if(p==father[p])
return p;
else
{
father[p]=find(father[p]);
return father[p];
}
}
void make_union(int p,int q)
{
int x,y;
x=find§;
y=find(q);
if(x!=y)
father[x]=y;
}
public:
int minMalwareSpread(vector<vector>& graph,vector& initial)
{
int minus_node=0,n=graph.size();
sort(initial.begin(),initial.end());
int result=initial[0];
for(int i=0;i<n;++i)
{
father[i]=i;
node_counter[i]=0;
source_counter[i]=0;
}
for(int i=0;i<n;++i)
{
for(int j=0;j<i;++j)
{
if(graph[i][j])
make_union(i,j);
}
}
for(int i=0;i<n;++i)
node_counter[find(father[i])]+=1;
for(int i=0;i<initial.size();++i)
source_counter[find(father[initial[i]])]+=1;
for(int i=0;i<initial.size();++i)
{
if(source_counter[find(father[initial[i]])]==1 && node_counter[find(father[initial[i]])]>minus_node)
{
minus_node=node_counter[find(father[initial[i]])];
result=initial[i];
}
}
return result;
}
};

//928尽量减少恶意软件的传播 II
/*

  • 在节点网络中,只有当 graph[i][j] = 1 时,每个节点 i 能够直接连接到另一个节点 j。
    一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件感染。这种恶意软件的传播将继续,直到没有更多的节点可以被这种方式感染。
    假设 M(initial) 是在恶意软件停止传播之后,整个网络中感染恶意软件的最终节点数。
    我们可以从初始列表中删除一个节点,并完全移除该节点以及从该节点到任何其他节点的任何连接。如果移除这一节点将最小化 M(initial), 则返回该节点。如果有多个节点满足条件,就返回索引最小的节点。

示例 1:
输出:graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
输入:0

示例 2:
输入:graph = [[1,1,0],[1,1,1],[0,1,1]], initial = [0,1]
输出:1

示例 3:
输入:graph = [[1,1,0,0],[1,1,1,0],[0,1,1,1],[0,0,1,1]], initial = [0,1]
输出:1 */

class Solution {
public:
int flag[305],num[305];
int pan[305],fnum[305];
void dfs(int root,int now,vector<vector>& graph){
int len = graph.size();
for(int i=0;i<len;i++){
if(graph[now][i]){
if(pan[i]||flag[i]==root)continue; //如果遇到另外一个源节点或者当前节点已被当前源节点感染
flag[i] = root;
fnum[i]++;
dfs(root,i,graph);
}
}
}

int minMalwareSpread(vector<vector<int>>& graph, vector<int>& initial) {
    memset(flag,-1,sizeof(flag)); memset(num,0,sizeof(num)); memset(pan,0,sizeof(pan));memset(fnum,0,sizeof(fnum));
    int sz = initial.size();
    for(int i=0;i<sz;i++)pan[initial[i]] = 1;
    for(int i=0;i<sz;i++){   //对于每一个源节点都搜一下
        int root = initial[i];
        flag[root] = root;num[root] = 1;
        fnum[root]++;
        dfs(root,root,graph);
    }
    int ma = 0;int ans = 0;
    int len = graph.size();
    for(int i=0;i<len;i++){  //如果只有一个源节点感染当前节点,那么对应的源节点单独感染数量增加
        if(fnum[i]==1)num[flag[i]]++;
    }
    sort(initial.begin(),initial.end()); //注意排序
    for(int i=0;i<sz;i++){
        if(num[initial[i]]>ma){
            ma = num[initial[i]];ans = initial[i];
        }
    }
    return ans;
}

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值