python class函数引用数据结构_python实现各种常用算法之数据结构(7)

python实现并查集的操作

并查集的介绍

并查集是一种数据结构,用于处理对 N 个元素的集合划分和判断是否属于同集合的问题。让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。

注:定义来自百度百科。

并查集的主要性质

用集合中的某个元素来代表这个集合,该元素称为集合的代表元。

一个集合内的所有元素组织成以代表元为根的树形结构。

对于每一个元素parent[x]指向 x 在树形结构上的父亲节点。如果 x 是根节点,则令 parent[x] = x。

对于查找操作,假设需要确定x 所在的的集合,也就是确定集合的代表元。可以沿着parent[x]不断在树形结构中向上移动,直到到达根节点。

判断两个元素是否属于同一集合,只需要看他们的代表元是否相同即可。

并查集的应用

1、维护无向图的连通性。支持判断两个点是否在同一连通块内,和判断增加一条边是否会产生环。

2、用在求解最小生成树的 Kruskal 算法里。

基本功能实现

创建 union_find 类

创建一个 union_find 的类,并初始化。初始化两个字典,一个保存节点的父节点,另外一个保存父节点的大小。初始化的时候,将节点的父节点设为自身,size 设为 1。

class union_find(object):

def __init__(self, data_list):

self.father_dict = {} # 保存节点的父节点

self.size_dict = {} # 保存父节点的大小

for node in data_list:

self.father_dict[node] = node

self.size_dict[node] = 1

添加 find (查)函数

使用递归的方式来查找父节点

def find(self, node):

father = self.father_dict[node]

if(node != father): # 递归查找父节点

father = self.find(father)

# 在查找父节点的时候,顺便把当前节点移动到父节点上面这个操作算是一个优化

self.father_dict[node] = father

return father

添加 is_same_set 函数

查看两个节点是不是在一个集合里面。通过调用 find 函数,判断两个节点是否是同一个父节点,如果是则判断两个节点属于一个集合。

def is_same_set(self, node_a, node_b):

return self.find(node_a) == self.find(node_b)

添加 union (并)函数

将两个集合合并在一起

def union(self, node_a, node_b):

# 对合并的两个节点做初步判断,判断是否为空

if node_a is None or node_b is None:

return

# 分别查找两个节点的父节点

a_head = self.find(node_a)

b_head = self.find(node_b)

# 当两个节点的父节点不一样时,才能做合并操作

if(a_head != b_head):

a_set_size = self.size_dict[a_head]

b_set_size = self.size_dict[b_head]

# 根据集合的大小做判断,尽量使小集合并到大集合

if(a_set_size >= b_set_size):

self.father_dict[b_head] = a_head

self.size_dict[a_head] = a_set_size + b_set_size

else:

self.father_dict[a_head] = b_head

self.size_dict[b_head] = a_set_size + b_set_size

实例应用

根据参考的 union_find ,完成以下功能的实现

在代码中实现这些步骤:

初始 a = [1,2,3,4,5],并将其添加到并查集里

分别合并[1,2] [3,5] [3,1]

然后判断 2 5 是否为同一个集合

代码实现

class union_find(object):

def __init__(self, data_list):

# 保存节点的父节点

self.father_dict = {}

# 保存父节点的大小

self.size_dict = {}

# 把传进来的数据,分别添加到两个字典中

for node in data_list:

self.father_dict[node] = node

self.size_dict[node] = 1

def find(self, node):

father = self.father_dict[node]

# 递归查找父节点

if (node != father):

father = self.find(father)

# 在查找父节点的时候,顺便把当前节点移动到父节点上面这个操作算是一个优化

self.father_dict[node] = father

return father

def is_same_set(self, node_a, node_b):

# 判断两个节点是否是同一父节点,是,则判断两个节点属于一个集合

return self.find(node_a) == self.find(node_b)

def print_dict(self):

# 输出两个字典中的数据

print(self.father_dict, self.size_dict)

def union(self, node_a, node_b):

# 对合并的两个节点做初步判断,判断是否为空

if node_a is None or node_b is None:

return

# 分别查找两个节点的父节点

a_head = self.find(node_a)

b_head = self.find(node_b)

# 当两个节点的父节点不一样时,才能做合并操作

if (a_head != b_head):

a_set_size = self.size_dict[a_head]

b_set_size = self.size_dict[b_head]

# 根据集合的大小做判断,尽量使小集合合并到大集合

if (a_set_size >= b_set_size):

# a的集合数目大于等于b的,把b的父节点改为a的头结点,即合并到a中去

self.father_dict[b_head] = a_head

# 修改a集合的数目大小

self.size_dict[a_head] = a_set_size + b_set_size

else:

self.father_dict[a_head] = b_head

self.size_dict[b_head] = a_set_size + b_set_size

if __name__ == '__main__':

a = [1, 2, 3, 4, 5]

union_find = union_find(a)

union_find.union(1, 2)

union_find.union(3, 5)

union_find.union(3, 1)

union_find.print_dict()

print(union_find.is_same_set(2, 5))

运行结果如下

解析

第一个字典中,存储的格式为【节点:父节点】

第二个字典中,存储的格式为【父节点:子节点的数目】

我们需要判断的是2与5是否是同一个代表元

根据第一个字典可得2的根节点查找路径为

2–1--3–3

5的根节点查找路径为

5–3

最后比较,两者的代表元相同为3,故返回True

你可能感兴趣的:(python,class函数引用数据结构)