contenu
1. Concepts connexes de la recherche syndicale
2. Opérations pertinentes de recherche syndicale et sa réalisation
1. Concepts connexes de la recherche syndicale
L'ensemble de recherche d'union est une structure de données arborescente utilisée pour traiter les problèmes de fusion et d'interrogation de certains ensembles disjoints.
L'idée de la recherche d'union est d'utiliser un tableau pour représenter l'ensemble de la forêt (parent), et le nœud racine de l'arbre identifie de manière unique un ensemble. Tant que nous trouvons la racine d'un élément, nous pouvons déterminer quel ensemble il est dans.
2. Opérations pertinentes de recherche syndicale et sa réalisation
L'idée de la recherche d'ensemble est de déterminer le groupe dans lequel se trouve le sommet par marquage.
Donc pour un graphe à n points et m arêtes, il faut créer un nouveau tableau f de longueur n (qui peut être compris comme père), et f[n] représente la "personne représentative" du gang au point n. deux points appartiennent au gang Si les "représentants" sont les mêmes, les deux points appartiennent au même gang.
Au début, chaque sommet est déconnecté l'un de l'autre, donc chaque sommet appartient à un gang seul, et chaque sommet doit être le "représentant" de son propre gang, donc nous fixons la valeur initiale de f[n] Attribuez-le à n .
Et et gang :
Par exemple, nous voulons fusionner 1 et 3 pour en faire un gang. Puisqu'il est devenu un gang, il doit y avoir un porte-parole, alors le parti le plus fort doit être le porte-parole. Maintenant, la force de 1 et 3 est la même. Supposons nous laissons 3 être le porte-parole :
Tout à l'heure, nous avons fusionné 3 et 1, maintenant nous devons fusionner 3 et 2. S'il est combiné selon f[a] = b, alors f[3] se voit attribuer la valeur 2. De cette manière, la valeur d'origine de f[3] 1 est écrasée, c'est-à-dire que les groupes de 1 et 3 sont "désassemblés" de force. Nous devons donc définir f[représentant du gang d'un] = (représentant du gang de b).
Déterminez si deux équipes sont représentées par le même :
Nous pouvons faire de même en vérifiant si leurs représentants sont égaux. Voir le code générique ci-dessous :
#include<unordered_map> #include<iostream> #include<stack> #include<list> #include<vector> using namespace std; template<class V> struct Node { Node(V v) :val(v) {} V val; }; template<class V> class UnionFind { public: UnionFind(int N) { for(int value=1;value<=N;value++){ Node<V>* node = new Node<V>(value);//创建一个节点 nodes.insert(make_pair(value, node));//插入 parent.insert(make_pair(node, node));//一开始自己指向自己 sizeMap.insert(make_pair(node, 1));//一开始集合的个数为1 } } Node<V>* findFather(Node<V>* cur) {//寻找其父亲节点 stack<Node<V>*>path; while (cur != parent[cur]) {//知道不能够再往上了 path.push(cur);//记录这条路径上的节点 cur = parent[cur];//一直往上走 } while (!path.empty()) {//路径压缩做一个优化我们每次都要找代表人都要一直往上找不如找一次找到过程我将其全部设置好 parent.insert(make_pair(path.top(), cur));//将其父亲节点都设置为cur path.pop(); } return cur;// } bool isSameSet(V a, V b) { if (!nodes[a] || !nodes[b]) {//看这两个集合是否都存在 return false; } return findFather(nodes[a]) == findFather(nodes[b]);//查看他们的父亲节点是否相同 } bool Union(V a, V b) { if (!nodes[a] || !nodes[b]) {//首先看其是否都存在 return false; } Node<V>* aHead = findFather(nodes[a]);//找到各自对应的父亲节点 Node<V>* bHead = findFather(nodes[b]); if (aHead != bHead) { int aSetSize = sizeMap[aHead]; int bSetSize = sizeMap[bHead]; if (aSetSize >= bSetSize){//a集合的大小大于b集合的大小 parent[bHead] = aHead; sizeMap[aHead] = aSetSize + bSetSize; sizeMap.erase(bHead); } else { parent[aHead] = bHead; sizeMap[bHead] = aSetSize + bSetSize;//合并 sizeMap.erase(aHead);//删除 } } return true; } private: unordered_map<V, Node<V>*>nodes;//存储节点 unordered_map<Node<V>*, Node<V>*>parent;//节点的父亲 unordered_map<Node<V>*, int>sizeMap;//每个一集合的个数 };
Examinons deux questions du JO :
Description du sujet:
Comme cela a été dit plus haut, seul le code est donné ici :
#include<unordered_map> #include<iostream> #include<stack> #include<list> #include<vector> using namespace std; class UnionFind{ public: UnionFind(int n){ parent.resize(n+1);//存储节点对应的父亲节点 size.resize(n+1);//存储集合对应的大小 help.resize(n);//做路径压缩的那个数组 sets=n; for(int i=1;i<=n;i++){ parent[i]=i;//一开始父亲节点 size[i]=1; } } int find(int i){ int hi=0; while(i!=parent[i]){ i=parent[i]; help[hi++]=i; } for(hi--;hi>=0;hi--){ parent[help[hi]]=i;//路径压缩 } return i; } bool isSameSet(int x,int y){ return find(x)==find(y); } void Union(int i,int j){ int aHead=find(i);//查找他的父亲 int bHead=find(j);//查找父亲 if(aHead!=bHead){//如果两个的父亲不相等 if(size[aHead]>=size[bHead]){//a集合元素大一点也就是力量大一点 size[aHead]+=size[bHead]; parent[bHead]=aHead; } else{ size[bHead]+=size[aHead]; parent[aHead]=bHead; } sets--; } } private: vector<int>parent;//存储父亲节点 vector<int>size;//集合的大小个数 vector<int>help;// int sets;//集合的个数 }; int main(){ int N,M; cin>>N>>M; UnionFind t(N); while(M--){ int opt,x,y; scanf("%d%d%d",&opt,&x,&y); if(opt==1){ if(t.isSameSet(x, y)){ printf("Yes\n"); } else{ printf("No\n"); } } else{ t.Union(x,y); } } return 0; }
Offre de points d'épée II 116. Nombre de provinces - LeetCode (leetcode-cn.com)
Description du sujet:
Idées de résolution de problèmes :
Selon le sens du titre, si isconectin[i][j] sont connectés, cela signifie qu'ils se connaissent, à ce stade, il suffit de les fusionner et de retourner finalement le nombre d'ensembles restants.
Code correspondant :
class Union { public: Union(int n) { size.resize(n); parent.resize(n); help.resize(n); sets=n; for(int i=0;i<n;i++) { parent[i]=i; size[i]=1; } } int findfather(int i) { int hi=0; while(parent[i]!=i) { help[hi++]=i;//将沿途路径所有的节点全部记录下来 i=parent[i]; } for(hi--;hi>=0;hi--)//路径压缩 { parent[help[hi]]=i; } return i;//返回父亲 } int getSets()//获取集合的数量 { return sets; } void unoin(int i,int j)//合并两个集合 { int aHead=findfather(i); int bHead=findfather(j); if(aHead!=bHead) { if(size[aHead]>=size[bHead]) { size[aHead]+=size[bHead]; parent[bHead]=aHead; } else { size[bHead]+=size[aHead]; parent[aHead]=bHead; } --sets; } } private: vector<int>size; vector<int>parent;//存储对应的父亲 vector<int>help;//路径压缩 int sets;//记录集合的数量 }; class Solution { public: int findCircleNum(vector<vector<int>>& isConnected) { int n=isConnected.size(); Union t(n); for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(isConnected[i][j]==1)//说明两个认识 { t.unoin(i,j);//合并他们两个 } } } return t.getSets();//返回剩余的集合数 } };