您的位置:首页 > 健康 > 美食 > 线上线下一体化营销_北京谷歌优化_网络优化主要做什么_百度发广告怎么发

线上线下一体化营销_北京谷歌优化_网络优化主要做什么_百度发广告怎么发

2024/12/23 16:58:28 来源:https://blog.csdn.net/weixin_70808146/article/details/144357794  浏览:    关键词:线上线下一体化营销_北京谷歌优化_网络优化主要做什么_百度发广告怎么发
线上线下一体化营销_北京谷歌优化_网络优化主要做什么_百度发广告怎么发

并查集解决什么问题

并查集常用来解决连通性问题

大白话就是当我们需要判断两个元素是否在同一个集合里的时候,我们就要想到用并查集。

原理

既然是查找是否在同一个集合中,那么这个集合是怎么构建的呢?用一维数组来表示一个有向图,对于father[u]=v,来说,u指向了v,那么假如father[v]=k,这意味u,v,k是连通的,所以如何表示连通呢?

bool isSame(int u,int v){u=find(u);//寻找father[u],即u的根(指向)v=find(v);if(u==v) return true;//意味着u,v他们指向了同一个结点,所以在一个集合中return false;
}

而这个find()其实就是递归的寻找father,直到找到根为止

int find(int u){if(u==father[u]) return u;//自己指向自己,根else return find(father[u]);
}

如果最后的根是自己指向自己,那么初始化就是这样

void init(){for(int i=1;i<=n;i++){//一般结点从1开始father[i]=i;}
}

那么集合如何构建呢?

void join(int u,int v){u=find(u);v=find(v);if(u==v) return;//根相同,没必要加入了father[v]=u;//设置根
}

路径压缩 

        在查找结点的根的时候,我们使用了递归的方式,但是查找次数边多之后,会发现这个过程是重复的,会浪费很多时间,就像这样,1<-2<-3<-4,如果我们查找4的根就需要递归好多次,如果我们变成这样呢?1<-2,1<-3,1<-4,每次查找根只需要一次就够了。这就是路径压缩。所以我们的find函数应该这样写

// 并查集里寻根的过程
int find(int u) {if (u == father[u]) return u;else return father[u] = find(father[u]); // 路径压缩
}

 

误区 

这些代码中join和isSame有一段很相似,但在join中不能调用isSame,就像这样

// 判断 u 和 v是否找到同一个根
bool isSame(int u, int v) {u = find(u);v = find(v);return u == v;
}// 将v->u 这条边加入并查集
void join(int u, int v) {if (isSame(u, v)) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回father[v] = u;
}

假设执行join(1,2),join(3,2),他的过程是这样的,

先查找1,2的根为 1,2,所以构建1<-2,但是在执行join(3,2)时,3的根是3,而2的根是1,那么在isSame中,这个函数返回false,意味着这两个值不在一个集合中。然而这并不对,我们想要的是这两个数在同一个集合中。

那么如果代码像之前那样呢?

在构建完1<-2之后,查找3的根是3,2的根是1,然后又构建了一个3<-1。这个时候再去执行isSame的话,会发现1的根是3,3的根是3,3==3,所以两个数在同一个集合中。

好了,原理大概就这样,总结一下,并查集的大致套路

int n = 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好
vector<int> father = vector<int> (n, 0); // C++里的一种数组结构// 并查集初始化
void init() {for (int i = 0; i < n; ++i) {father[i] = i;}
}
// 并查集里寻根的过程
int find(int u) {return u == father[u] ? u : father[u] = find(father[u]); // 路径压缩
}// 判断 u 和 v是否找到同一个根
bool isSame(int u, int v) {u = find(u);v = find(v);return u == v;
}// 将v->u 这条边加入并查集
void join(int u, int v) {u = find(u); // 寻找u的根v = find(v); // 寻找v的根if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回father[v] = u;
}

既然如此,那么就来刷一道题

题目:寻找存在的路径

107. 寻找存在的路径 (kamacoder.com) 

输入描述

第一行包含两个正整数 N 和 M,N 代表节点的个数,M 代表边的个数。 

后续 M 行,每行两个正整数 s 和 t,代表从节点 s 与节点 t 之间有一条边。 

最后一行包含两个正整数,代表起始节点 source 和目标节点 destination。

输出描述

输出一个整数,代表是否存在从节点 source 到节点 destination 的路径。如果存在,输出 1;否则,输出 0。

 题目分析:

        标准的并查集问题

#include<iostream>
#include<vector>using namespace std;int n;
vector<int> father=vector<int>(101,0);void Init(){for (int i = 1; i <= n; i++)  father[i] = i;
}
int find(int u){if(u==father[u]) return u;return father[u]=find(father[u]);//递归查找根,这一步进行了路径压缩,将所有结点指向根
}void join(int u,int v){u=find(u);v=find(v);if(u==v) return;//具有相同的根,同一个集合father[v]=u;//把v作为u的父节点加入集合
}
bool isSame(int u,int v){u=find(u);v=find(v);return u==v;
}int main(){int m,s,t,source,destination;cin>>n>>m;Init();while(m--){cin>>s>>t;join(s,t);}cin >> source >> destination;if (isSame(source, destination)) cout << 1 << endl;else cout << 0 << endl;
}

对于更详细的解析与其他语言的代码块,可以去代码随想录上查看。

代码随想录 (programmercarl.com)

已刷题目:139

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com