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