C++ 并查集2(带路径压缩)

本文介绍了如何使用C++编程实现并查集数据结构,并重点讲解了路径压缩的技术细节,提供了相应的测试案例。
摘要由CSDN通过智能技术生成

一、实现程序:

#include <iostream>
using namespace std;

struct Node { // 并查集结点类
    int data; // 保存数据
    int parent; // 保存父结点
};

class UnionFindSets {
public:
    UnionFindSets(int w[], int n); // 构造函数
    ~UnionFindSets(); // 析构函数
    void Union(int a, int b); // 并
    bool Find(int a, int b); // 查找两个数是否在同一集合
private:
    Node *s; // 数组
    int currentSize; // 实际存储的个数
    int Find(int x); // 查找x,并返回x的根结点
    int CollapsingFind(int i); // 路径压缩
};

// 构造函数
UnionFindSets::UnionFindSets(int w[], int n) {
    // 初始化
    currentSize = n;
    s = new Node[n];
    for(int i = 0; i < n; i++) {
        s[i].data = w[i];
        s[i].parent = -1;
    }
}

// 析构函数
UnionFindSets::~UnionFindSets() {
    delete []s; // 释放空间
}

// 并:根结点保存(-结点数),是结点的相反数,一个负数
void UnionFindSets::Union(int a, int b) {
    int root1, root2;
    
    root1 = Find(a); // 找到a的根结点
    root2 = Find(b); // 找到b的根结点
    if(root1 == root2 || root1 == -1 || root2 == -1) // 根结点相同,或者其中一个数不在集合中
        return;
    // 根结点不同,才将两棵树合并
    if(s[root1].parent <= s[root2].parent) { // 说明root1的结点数比root2的结点数大或相同
        s[root2].parent = root1; // 将root2树合并到root1树中
        s[root1].parent = s[root1].parent + s[root2].parent; // 保存结点数
    }
    else { // root1的结点数比root2的结点数少
        s[root1].parent = root2;
        s[root2].parent = s[root1].parent + s[root2].parent; // 保存结点数
    }
}

// 查找两个数是否在同一集合
bool UnionFindSets::Find(int a, int b) {
    int root1, root2;
    
    root1 = Find(a); // 查找a的根结点
    root2 = Find(b);
    if(root1 != root2) // 根结点不同,说明不在同一集合
        return false;
    return true; // 在同一集合,返回true
}

// 查找x,并返回x的根结点
int UnionFindSets::Find(int x) {
    int i;
    
    for(i = 0; i < currentSize && s[i].data != x; i++); // 在数组中查找
    if(i >= currentSize) // 没找到
        return -1;
    i =  CollapsingFind(x); //  找根结点,并进行路径压缩
    return i;
}

// 路径压缩:对结点i这条路径上的结点,将根结点变为各个结点的父结点
int UnionFindSets::CollapsingFind(int i) {
    int j, temp;
    
    for(j = i; s[j].parent >= 0; j = s[j].parent); // 往上找根结点
    while(i != j) { // 当父结点不为根
        temp = s[i].parent; // 保存i的当前父结点
        s[i].parent = j; // 把根结点变为i的父结点
        i = temp; // 对i的原父结点也做路径压缩,直到父结点为根结点为止
    }
    return j;
}

int main(int argc, const char * argv[]) {
    int w[] = {3, 8, 10, 1, 5, 4}, choice, a, b;
    int len = sizeof(w)/sizeof(w[0]); // 数组的长度
    bool finished = false;
    
    UnionFindSets uf(w, len); // 创建并查集对象
    while(!finished) {
        cout << "[1]并" << endl;
        cout << "[2]查" << endl;
        cout << "[3]退出" << endl;
        cout << "请输入你的选择[1-3]:";
        cin >> choice;
        switch(choice) {
            case 1:
                cout << "请输入两个要进行并运算的元素:" << endl;
                cin >> a >> b;
                uf.Union(a, b); // 并
                break;
            case 2:
                cout << "请输入两个要检查是否在同一集合的元素:" << endl;
                cin >> a >> b;
                if(uf.Find(a, b)) // 是
                    cout << "Yes" << endl;
                else // 否
                    cout << "No" << endl;
                break;
            case 3:
                finished = true;
                break;
            default:
                cout << "输入错误,请重新输入!" << endl;
        }
    }
    return 0;
}

测试:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值