1. 程式人生 > >《演算法》第四版algs4:union-find並查集C++實現

《演算法》第四版algs4:union-find並查集C++實現

github地址:https://github.com/Nwpuer/algs4-in-cpp
QuickFindUF實現(在檔案"quick_find_uf"中)

#pragma once 
#include <vector>
#include <string> 
#include <stdexcept>
#include <iostream>


class QuickFindUF {
private:
    std::vector<size_t> id;
    size_t count;
public: 
    QuickFindUF(size_t n):count(n) {
        id.reserve(n);//improve the performance
        for (size_t i = 0; i < n; ++i)
            id.push_back(i);
    }

    size_t Count() const {
        return count;
    }

    bool Connected(size_t p, size_t q) const {
        return Find(p) == Find(q);
    }

    size_t Find(size_t p) const {
        Validate(p);
        return id[p];
    }

    void Union(size_t p, size_t q) {
        Validate(p);
        Validate(q);

        auto pID = id[p];
        auto qID = id[q];

        if (pID == qID) return;

        for (size_t i = 0; i < id.size(); ++i) 
            if (id[i] == pID) 
                id[i] = qID;
        --count;
    }

private: 
    void Validate(size_t p) const {
        if (p >= id.size())
            throw std::out_of_range("index out of range");
    }

public:
    static void MainTest() {
        size_t n;
        std::cin >> n;
        QuickFindUF uf(n);
        size_t p, q;
        while (std::cin >> p >> q) {
            if (uf.Connected(p, q)) continue;
            uf.Union(p, q);
            std::cout << p << " " << q << std::endl;
        }
        std::cout << uf.Count() << " components" << std::endl;
    }
};

QuickUnionUF實現(在檔案"quick_union_uf"中)

#pragma once 
#include <vector>
#include <string> 
#include <stdexcept>
#include <iostream>


class QuickUnionUF {
private:
    std::vector<size_t> id;
    size_t count;
public: 
    QuickUnionUF(size_t n):count(n) {
        id.reserve(n);//improve the performance
        for (size_t i = 0; i < n; ++i)
            id.push_back(i);
    }
    
    size_t Count() const {
        return count;
    }

    bool Connected(size_t p, size_t q) const {
        return Find(p) == Find(q);
    }

    size_t Find(size_t p) const {
        Validate(p);
        while (p != id[p])
            p = id[p];
        return p;
    }

    void Union(size_t p, size_t q) {
        Validate(p);
        Validate(q);

        auto pRoot = Find(p);
        auto qRoot = Find(q);

        if (pRoot == qRoot) return;

        id[pRoot] = qRoot;
        --count;
    }

private: 
    void Validate(size_t p) const {
        if (p >= id.size())
            throw std::out_of_range("index out of range");
    }

public:
    static void MainTest() {
        size_t n;
        std::cin >> n;
        QuickUnionUF uf(n);
        size_t p, q;
        while (std::cin >> p >> q) {
            if (uf.Connected(p, q)) continue;
            uf.Union(p, q);
            std::cout << p << " " << q << std::endl;
        }
        std::cout << uf.Count() << " components" << std::endl;
    }
};

WeightedQuickUnionUF實現(在檔案"weighted_quick_union_uf"中)

#pragma once 
#include <vector>
#include <string> 
#include <stdexcept>
#include <iostream>


class WeightedQuickUnionUF {
private:
    std::vector<size_t> id;
    size_t count;
    std::vector<size_t> sz;
public: 
    WeightedQuickUnionUF(size_t n):count(n) {
        id.reserve(n);//improve the performance
        for (size_t i = 0; i < n; ++i)
            id.push_back(i);
        sz.reserve(n);
        for (size_t i = 0; i < n; ++i)
            sz.push_back(1);
    }

    size_t Count() const {
        return count;
    }

    bool Connected(size_t p, size_t q) const {
        return Find(p) == Find(q);
    }

    size_t Find(size_t p) const {
        Validate(p);
        while (p != id[p])
            p = id[p];
        return p;
    }

    void Union(size_t p, size_t q) {
        Validate(p);
        Validate(q);

        auto pRoot = Find(p);
        auto qRoot = Find(q);

        if (pRoot == qRoot) return;

        if (sz[pRoot] < sz[qRoot]) {
            id[pRoot] = qRoot;
            sz[pRoot] += sz[qRoot];
        }
        else {
            id[qRoot] = pRoot;
            sz[pRoot] += sz[qRoot];
        }
        --count;
    }

private: 
    void Validate(size_t p) const {
        if (p >= id.size())
            throw std::out_of_range("index out of range");
    }

public:
    static void MainTest() {
        size_t n;
        std::cin >> n;
        WeightedQuickUnionUF uf(n);
        size_t p, q;
        while (std::cin >> p >> q) {
            if (uf.Connected(p, q)) continue;
            uf.Union(p, q);
            std::cout << p << " " << q << std::endl;
        }
        std::cout << uf.Count() << " components" << std::endl;
    }
};

測試程式碼(檔案"uf_test.cpp")

#include <iostream>
#include <fstream>
#include "quick_find_uf.h"
#include "quick_union_uf.h"
#include "weighted_quick_union_uf.h"

int main() {
    std::ifstream in("../data/tinyUF.txt");
    std::cin.rdbuf(in.rdbuf());//重定向cin

    //QuickFindUF::MainTest();
    //QuickUnionUF::MainTest();
    WeightedQuickUnionUF::MainTest();
}

程式碼實現的時候的幾個注意點:
1.將介面的首字母大寫,因為介面union()與C++關鍵字union衝突。為防止以後還出現這種情況,以後介面的首字母都大寫。
2.id_中的資料使用size_t而不用int的原因:
size_t是一個sizeof()返回的型別,是一個無符號數,大小足以容納任何序列的長度,也就是如論你序列有多長,它的大小都可以用size_t來表示。具有跨平臺的特性。這裡將id裡面的數宣告為size_t是因為在類的初始化成員函式中,每個位置的值都是它所在的索引,所以值是與vector的大小相關的。用int不太合適,因為可能會溢位,用size_t更好。
size_t其實就是size_type,是一個表示大小,任何大小都可以表示,當有數數兒的行為的時候,並且確保不為負數,適合用它。
3.這裡添加了一個Validate()函式,防止越界。JAVA中陣列越界會自動被檢測出來,所以書中原始碼不需要檢測;而C++中不會,所以需要我們額外關注。

在瞭解完如何實現一個並查集後,你也許想要實踐一下,不如看看:
https://blog.csdn.net/weixin_43462819/article/details/83628052