1. 程式人生 > >資料結構實現 9.1:並查集_陣列結構實現(C++版)

資料結構實現 9.1:並查集_陣列結構實現(C++版)

資料結構實現 9.1:並查集_陣列結構實現(C++版)

1. 概念及基本框架

並查集 是一種高階資料結構,主要用於表示元素間的關係,即元素是否歸屬於同一個集合。

並查集

如上圖,不同的顏色表示不同的集合,裡面的數字表示元素儲存的資料。下面給出並查集的基本介面。

class UnionFind{
public: virtual int size() = 0; virtual bool isEmpty() = 0; //是否連線 virtual bool isConnected(int p, int q) = 0; //聯合元素 virtual void unionElements(int p, int q) = 0; };

我們使用陣列來實現並查集,這裡我們忽略每個元素的資料,用每個元素的階表示元素本身,用陣列中存的數字表示元素所屬集合。那麼並查集的實現過程如下:
1.將每個元素初始化成自身,在下面的建構函式中實現。
2.聯合,將需要聯合的元素中儲存的數字統一為一個。
3.查詢,只要陣列中儲存的數字是一樣的,那麼認為兩個元素屬於同一個集合。

class QuickFind : public UnionFind{
public:
	QuickFind(int size){
		m_data = new int[size];
		for (int i = 0; i < size; ++i){
			m_data[i] = i;
		}
		m_size = size;
	}
	...
private:
	int *m_data;
	int m_size;
};

m_data 用來表示陣列。
m_size 表示並查集大小。
接下來我們就對並查集的聯合、查詢以及一些其他基本操作用程式碼去實現。

2. 基本操作程式實現

2.1 聯合操作

class QuickFind : public UnionFind{
public:
	...
	//聯合元素
	void unionElements(int p, int q){
		if (p == q){
			return;
		}
		for (int i = 0; i < m_size; ++i){
			if (m_data[i] == m_data[q]){
				m_data[i] = m_data[p];
			}
		}
	}
	...
};

聯合元素時,需要遍歷一遍陣列,然後把需要聯合的元素都聯合起來。

2.2 查詢操作

class QuickFind : public UnionFind{
public:
	...
	//是否連線
	bool isConnected(int p, int q){
		return find(p) == find(q);
	}
	...
private:
	int find(int index){
		if (index < 0 || index >= m_size){
			cout << "訪問越界!" << endl;
			throw 0;
		}
		return m_data[index];
	}
	...
};

用於查詢兩個元素是否被聯合在一起。

2.3 其他操作

線段樹還有一些其他的操作,包括 並查集大小 等的查詢操作。

class QuickFind : public UnionFind{
public:
	...
	int size(){
		return m_size;
	}
	bool isEmpty(){
		return m_size == 0;
	}
	void print(){
		cout << "QuickFind: " << "Size = " << m_size << endl;
		cout << '[';
		for (int i = 0; i < m_size; ++i){
			cout << m_data[i];
			if (i != m_size - 1){
				cout << ',';
			}
		}
		cout << ']' << endl;
	}
	...
};

3. 演算法複雜度分析

3.1 聯合操作

函式 最壞複雜度 平均複雜度
unionElements O(n) O(n/2) = O(n)

因為要遍歷一次陣列,所以聯合操作的時間複雜度為 O(n)

3.2 查詢操作

函式 最壞複雜度 平均複雜度
isConnected O(1) O(1)

總體情況:

操作 時間複雜度
O(n)
O(1)

4. 完整程式碼

程式完整程式碼(這裡使用了標頭檔案的形式來實現類)如下:
虛擬函式介面 程式碼如下:

#ifndef __UNIONFIND_H__
#define __UNIONFIND_H__

class UnionFind{
public:
	virtual int size() = 0;
	virtual bool isEmpty() = 0;
	//是否連線
	virtual bool isConnected(int p, int q) = 0;
	//聯合元素
	virtual void unionElements(int p, int q) = 0;
};

#endif

並查集 類程式碼:

#ifndef __QUICKFIND_H__
#define __QUICKFIND_H__

#include "UnionFind.h"

class QuickFind : public UnionFind{
public:
	QuickFind(int size){
		m_data = new int[size];
		for (int i = 0; i < size; ++i){
			m_data[i] = i;
		}
		m_size = size;
	}
	int size(){
		return m_size;
	}
	bool isEmpty(){
		return m_size == 0;
	}
	void print(){
		cout << "QuickFind: " << "Size = " << m_size << endl;
		cout << '[';
		for (int i = 0; i < m_size; ++i){
			cout << m_data[i];
			if (i != m_size - 1){
				cout << ',';
			}
		}
		cout << ']' << endl;
	}
	//是否連線
	bool isConnected(int p, int q){
		return find(p) == find(q);
	}
	//聯合元素
	void unionElements(int p, int q){
		if (p == q){
			return;
		}
		for (int i = 0; i < m_size; ++i){
			if (m_data[i] == m_data[q]){
				m_data[i] = m_data[p];
			}
		}
	}
private:
	int find(int index){
		if (index < 0 || index >= m_size){
			cout << "訪問越界!" << endl;
			throw 0;
		}
		return m_data[index];
	}
private:
	int *m_data;
	int m_size;
};

#endif