PAT甲級1107 Social Clusters (30分) 以及 牛客-並查集的實現 ,以及速度改進

牛客並查集 先練練手,因爲有測試數據

鏈接:https://www.nowcoder.com/questionTerminal/e7ed657974934a30b2010046536a5372
來源:牛客網

給定一個沒有重複值的整形數組arr,初始時認爲arr中每一個數各自都是一個單獨的集合。請設計一種叫UnionFind的結構,並提供以下兩個操作。
boolean isSameSet(int a, int b): 查詢a和b這兩個數是否屬於一個集合
void union(int a, int b): 把a所在的集合與b所在的集合合併在一起,原本兩個集合各自的元素以後都算作同一個集合
[要求]
如果調用isSameSet和union的總次數逼近或超過O(N),請做到單次調用isSameSet或union方法的平均時間複雜度爲O(1)

輸入描述:
第一行兩個整數N, M。分別表示數組大小、操作次數
接下來M行,每行有一個整數opt
若opt = 1,後面有兩個數x, y,表示查詢(x, y)這兩個數是否屬於同一個集合
若opt = 2,後面有兩個數x, y,表示把x, y所在的集合合併在一起

輸出描述:
對於每個opt = 1的操作,若爲真則輸出"Yes",否則輸出"No"
示例1
輸入
4 5
1 1 2
2 2 3
2 1 3
1 1 1
1 2 3
輸出
No
Yes
Yes
說明
每次2操作後的集合爲
({1}, {2}, {3}, {4})
({1}, {2, 3}, {4})
({1, 2, 3}, {4})

備註:
在這裏插入圖片描述

牛客並查集最初思路

並查集之前的實現也都是兩個函數,一個查找結合頭領的函數,一個合併集合的函數。

這裏N是在1到10^6之間,所以直接聲明個全局數組,全部賦值爲-1。

int pre[1000001];// 1-  N 不使用0  
// 在main函數裏面 fill(pre,pre+1000001,-1); 全設置成-1

int findP(int a)  //查找a所在集合的頭領
{
    int pos=a;//保存a的值是爲了下面的縮短路徑
    
    while(pre[a]!=-1)  //這兩行很容易考慮到,每一次找上一級,直到集合的頭領,sn[頭領]=-1
        a=pre[a];
	//這是a保存的就是集合的頭領編號了
    while(pre[pos]!=-1) //從原先a到 頭領之間的所有小頭目都需要把上一級直接修改爲頭領
    {
        int curP=pre[pos];//保存上一級編號
        pre[pos]=a;//修改當前編號的上一級爲集合頭領
        pos=curP;
    }
    return a;
}


void union2(int a,int b)
{
    if(!isSameSet(a,b))
    {
        //a的父親是b的父親了
        int ap=findP(a);
        sn[ap]=findP(b);
        findP(a);//這裏是因爲a所在的集合頭領變爲b所在集合的頭領了,所以a到原先頭領之間的小頭目都需要把首領改爲b集合的首領
    }
}

總AC代碼

#include <iostream>
#include <string>
using namespace std;
#include<algorithm>
#include<queue>
int pre[1000001];// 1-  N 不使用0
int findP(int a)
{
    int pos=a;
    while(pre[a]!=-1)
        a=pre[a];
    while(pre[pos]!=-1)
    {
        int curP=pre[pos];
        pre[pos]=a;
        pos=curP;
    }
    return a;
}
bool isSameSet(int a, int b)
{
    int ap=findP(a);
    int bp=findP(b);
    if(ap!=bp)
    {
        return false;
    }
    else
        return true;
}

void union2(int a,int b)
{
    if(!isSameSet(a,b))
    {
        //a的父親是b的父親了
        int ap=findP(a);
        pre[ap]=findP(b);
        findP(a);
    }
}




int main()
{
    fill(pre,pre+1000001,-1);
    int M,N;
    cin>>N>>M;
    for(int i=0;i<M;i++){
        int op;
        int a,b;
        scanf("%d %d %d",&op,&a,&b);
        if(op==1){
            if(isSameSet(a,b)==1)
                cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;
        }
        else{
            union2(a,b);
        }
    }
    return 0;
}


牛客並查集時間改進(可以直接看這個)

之後在牛客的測試發現,上述的代碼的時間有時候能AC有時候不能AC 小無奈,優化了一下。
並查集之前的實現也都是兩個函數,一個查找結合頭領的函數,一個合併集合的函數。

這裏N是在1到10^6之間,所以直接聲明個全局數組pre,pre[i] 保存編號i的頭領,默認自己是自己的頭領。
主要改進在findP函數,因爲自己是自己的首領了,直接遞歸調用搞定,很簡單,而且時間效率更高(這其實我有點不太理解,因爲我感覺遞歸時間效率並不高)

#include <iostream>
#include <string>
using namespace std;
#include<algorithm>
#include<queue>
int pre[1000001];// 1-  N 不使用0
int findP(int x)
{
    if (x != pre[x]) pre[x] = findP(pre[x]);
    return pre[x];
}



void union2(int a,int b)
{

    //a的父親是b的父親了
    int ap=findP(a);
    int bp=findP(b);
    pre[ap]=findP(b);
    findP(a);

}



int main()
{

    int M,N;
    cin>>N>>M;
    for(int i=1; i<=N; i++)
    {
        pre[i]=i;
    }
    for(int i=0; i<M; i++)
    {
        int op;
        int a,b;
        scanf("%d %d %d",&op,&a,&b);
        if(op==1)
        {
            if(findP(a)==findP(b))
                cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;
        }
        else
        {
            union2(a,b);
        }
    }
    return 0;
}



PAT

1107 Social Clusters (30分)
When register on a social network, you are always asked to specify your hobbies in order to find some potential friends with the same hobbies. A social cluster is a set of people who have some of their hobbies in common. You are supposed to find all the clusters.

Input Specification:
Each input file contains one test case. For each test case, the first line contains a positive integer N (≤1000), the total number of people in a social network. Hence the people are numbered from 1 to N. Then N lines follow, each gives the hobby list of a person in the format:

K
​i
​​ : h
​i
​​ [1] h
​i
​​ [2] … h
​i
​​ [K
​i
​​ ]

where K
​i
​​ (>0) is the number of hobbies, and h
​i
​​ [j] is the index of the j-th hobby, which is an integer in [1, 1000].

Output Specification:
For each case, print in one line the total number of clusters in the network. Then in the second line, print the numbers of people in the clusters in non-increasing order. The numbers must be separated by exactly one space, and there must be no extra space at the end of the line.

Sample Input:
8
3: 2 7 10
1: 4
2: 5 3
1: 4
1: 3
1: 4
4: 6 8 1 5
1: 4
Sample Output:
3
4 3 1

做了牛客那道題,再看這題其實只要 考慮一件事,如何把興趣一樣的人關聯起來,我是設置了一個 hobby[1001]={0} 然後只要hobby[興趣編號]==0 就把hobby[興趣編號]=人的編號 ,這樣每一個性趣編號我們就有了一個頭領了,然後之後的人的編號就直接用並查集的合併函數就OK了

然後輸出的時候我不知道怎麼想的 人家map的有序是指的key有序,不是value有序,以爲那裏思路不對,驗證了好久。。。。無奈

AC代碼

#include <iostream>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;

int pre[1001];// 1-  N 不使用0
int findP(int x)
{
    if (x != pre[x]) pre[x] = findP(pre[x]);
    return pre[x];
}



void union2(int a,int b)
{

    //a的父親是b的父親了
    int ap=findP(a);
    int bp=findP(b);
    pre[ap]=bp;
    findP(a);//這一步其實用不用都行,因爲後面會吧所有人的編號在進行一次find 壓縮路徑

}

int main()
{
    int N;
    cin>>N;
    //初始化並查集使用的首領數組
    for(int i=1; i<=N; i++)
    {
        pre[i]=i;
    }

    int hobby[1001]= {0};
    for(int i=1; i<=N; i++)
    {
        int Num;
        scanf("%d: ",&Num);
        while(Num>0)
        {
            Num--;
            int get;
            cin>>get;

            if(hobby[get]==0)
                hobby[get]=i;
            else{
                 union2(hobby[get],i);
            }
           


        }
    }
    for(int i=1; i<=N; i++)
    {
        findP(i);
    }

    //輸出
    map<int,int> mapp;
    for(int i=1; i<=N; i++)
    {
        mapp[pre[i]]++;
    }
    cout<<mapp.size()<<endl;

    vector<int> vec;
    for(auto it=mapp.rbegin(); it!=mapp.rend(); it++)
    {
        vec.push_back(it->second);

    }
    sort(vec.begin(),vec.end());
    for(int i=vec.size()-1;i>=0;i--){
        if(i==vec.size()-1){
            cout<<vec[i];
        }
        else
            cout<<' '<<vec[i];
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章