并查集类型的题目在力扣上刷题的时候经常会碰到,这个知识点,如果不懂,解题可能会很费劲,但若是掌握了并查集,解此类型的题目几乎都是套模板,说这么多了,并查集究竟是来干什么的呢,就个人理解来看,它是对数据用来进行归类的,初始状态下,我们假使每个数据都是独立的,在对这个数据进行查找类别的时候,初始时都是他们自身。并查集对外提供两个API,一个是查找find,一个是合并union,查找是查找出这个元素是属于哪个门类,合并,是合并两个元素为一个门类。它对应的数据结构如下:
function DSU(arr){
let re = {};
for(let i=0;i<arr.length;i++){
re[arr[i]]= arr[i];
}
this.find = function(k){
if(re[k]!=k){
re[k] = this.find(re[k]);
}
return re[k];
}
this.union = function(a,b){
let pa = this.find(a);
let pb = this.find(b);
if(pa!=pb){
re[pa] = pb;
}
}
}
好,我们现在看一道经典的并查集的题目,朋友圈问题
思路:这样的题目有特定的几个步骤,第一个是初始化我们的并查集,我们可以将每个学生标识为0-(N-1),然后就是将是朋友的学生进行union操作,最后在判断有几个朋友圈的时候,我们可以通过一个set来存储,最后返回set的长度即可,代码如下:
function DSU(N){
let re =[];
for(let i=0;i<N;i++){
re.push(i);
}
this.find = function(k){
if(re[k]!=k){
re[k] = this.find(re[k]);
}
return re[k];
}
this.union = function(a,b){
let pa = this.find(a);
let pb = this.find(b);
if(pa!=pb){
re[pa] = pb;
}
}
}
function findCircleNum(M){
let len = M.length;
if(len ==1){
return 1;
}
let k = new Set();
let dsu = new DSU(len);
for(let i=0;i<len;i++){
for(let j=i+1;j<len;j++){
if(M[i][j]==1){
dsu.union(i,j);
}
}
}
for(let i=0;i<len;i++){
k.add(dsu.find(i));
}
return k.size;
}
像这样的题,还有婴儿名字,等式方程的可能性,都是相似的解法,接下来我们再来看一个稍微进阶版的题目
这道题当然可以不用并查集去解决,我在不了解并查集的时候,解此道题的思路是用一个set去存放每个值,然后遍历每个数组中的值,当在set中没有这个数的前一个值时,那说明这个值是起点,然后往后在set里面寻找value+1,直到没找到,代码如下:
int longestConsecutive(vector<int>& nums) {
set<int> s;
for(int i=0;i<nums.size();i++){
s.insert(nums[i]);
}
set<int>::iterator it;
int maxlen = 0;
for(it = s.begin();it!= s.end();it++){
if(!s.count(*it-1)){
int cur = *it;
int len = 1;
while(s.count(cur+1)){
cur++;
len++;
}
maxlen = max(maxlen,len);
}
}
return maxlen;
}
今天我们也可以用并查集的思路来解决这个问题,在解决这道题的时候与上面几道题有点不同,为了方便查找到某个值连续后的最大值,我们可以在初始化,将每个值+1赋给这个值,举个例子当数组是3,4,8的时候,可以对应的map为{3:4 ,4:5, 8:9},在find的时候,如果当我们查找为3的时候,我们最后得到的值为5,当我们最后查找8的时候,最后得到的值为9。这样将最后值与当前值就可求出连续最长的数组长度了。代码如下:
var longestConsecutive = function(nums) {
let s = {};
let len = nums.length;
for(let i=0;i<len;i++){
s[nums[i]]=nums[i]+1;
}
function find(x){
if(s[x]==undefined){ // 递归出界条件
return x;
}
s[x] = find(s[x]);
return s[x];
}
let maxLen = 0;
for(let i=0;i<len;i++){
let num = nums[i];
let p = find(num);
maxLen = Math.max(maxLen,p-num);
}
return maxLen;
};
其实刷题很多时候就是要多思考,对于某个知识点深入思考了,认真解决一道后,这类型的问题就不成多大问题了。