代码随想录算法训练营第56天| 并查集 110.冗余连接 109.冗余连接II

110.冗余连接

思路:正着输入 倒着访问。如果访问的这两个元素根相同的话就删除这条边。如果新输入的s和t在集合中已经出现过,说明这条边多余了,可以直接删除。

题目链接:110.冗余连接

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

vector<int> father(1005,0);

int find(int s){
    if(s==father[s]) return s;
    else return find(father[s]);
}

void join(int s, int t){
    s=find(s);
    t=find(t);
    if(s==t) return;
    father[t]=s;
}

bool issame(int s, int t){
    s=find(s);
    t=find(t);
    return s==t;
}

int main(){
    int n,s,t;
    cin>>n;
    for(int i=0;i<=n;i++){
        father[i]=i;
    }
    
    for(int i=0;i<n;i++){
        cin>>s>>t;
        if(issame(s,t)){
             cout<<s<<" "<<t;
             return 0;
        }
        else join(s,t);
    }

    return 0;
}

109.冗余连接II

题目链接:109.冗余连接II

思路:三种情况 首先是入度为2,一个是两边都可以删,一个是只能删一边,另一个是出现环。怎么判断能不能删:如果碰到将要输入并查集的两个元素已经在并查集里了,说明成环,删错了。先存后面的,这样就先判断的是后面的,后面的不符合条件就删前面的。两边都可以删 删后面那个边。只能删一边 如果符合条件则删这个 不符合删另一个。剩下的细节写在注释里了。写的时候对于每个数组的含义认识比较模糊。

代码:

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

int n;
vector<int> father(1005,0);

int find(int s){//找到元素的根
    if(s == father[s]) return s;
    else return find(father[s]);
}

void init(){//初始化
    for(int i=0;i<=n;i++){
        father[i]=i;
    }
}

bool isSame(int s, int t){//判断两个元素是否属于同一个集合
    s = find(s);
    t = find(t);
    return s == t;
}

void join(int s, int t){//将两个元素放入同一个集合
    s = find(s);
    t = find(t);
    if(s == t) return;
    else father[s] = t;
}

bool isTreeAfterRemove(vector<vector<int>> &edges, int deleteedge){//判断删除一条边后是不是树
    init();
    for(int i=0;i<n;i++){
        if(i == deleteedge) continue; //碰到那条边就跳过
        if(isSame(edges[i][0],edges[i][1])) return false;
        //碰到相等说明都属于一个集合 已经成环 该情况不可以
        join(edges[i][0], edges[i][1]);
    }
    return true;
}
void getRemoveEdge(vector<vector<int>> &edges){//在成环的图中找到需要删除的边
    init();
    for(int i=0; i < n; i++){
        if(isSame(edges[i][0],edges[i][1])){
            cout << edges[i][0] << " " << edges[i][1];
            return;
        }
        else join(edges[i][0],edges[i][1]);
    } 
    
}

int main(){
    init();
    cin>>n;
    vector<int> inDegree(n+1);
    vector<vector<int>> edges;
    //计算每个节点的入度
    int s,t;
    for(int i=0;i<n;i++){
        cin >> s >> t;
        inDegree[t]++;
        edges.push_back({s,t});//存储每一条边
    }
    vector<int> vec;
    //碰到入度为2的点时 存储这两条边
    for(int i=n-1;i>=0;i--){//倒序方便访问最后一条边
        if(inDegree[edges[i][1]] == 2){//edges[i][1]表示边的终点
            vec.push_back(i);
        }
    }
    if(vec.size()>0){
        if(isTreeAfterRemove(edges,vec[0])){
            cout << edges[vec[0]][0] << " " << edges[vec[0]][1];
            return 0;
        }
        else cout << edges[vec[1]][0] << " " << edges[vec[1]][1];
        return 0;
    }
    
    getRemoveEdge(edges);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值