树论_并查集

28 阅读2分钟

利用双亲表示法可以追溯到某个节点所在的树的根节点

结构

利用线性表存储,每个节点含一个==data==域,一个==parent==域,若用的是顺序表存储且所有节点的data是递增的,可以直接用==数组下标==表示数据,省去data域

parent域就是指向这个节点的父节点在数组中的下标

根节点的parent域设置为==负数==(如-1)

struct TreeNode{
	DataType data;
	int parent;
}

TreeNode nodes[MaxSize];

查找根节点

  • 沿着parent域追溯到某个parent域为负的节点,该点就是根节点
  • 假设给定了需要查找根节点的节点的下标为i
int findRoot(int i,TreeNode nodes[]){
	while(nodes[i].parent>=0){i=nodes[i].parent};
	return i;
}

合并两个集合

  • 先查找这两个节点(下标为p,q)所在的==集合==是否是同一个,即是否是同一个==根节点==
  • 若是同一个集合,不做任何操作,否则将一个集合的根节点合并到另一个集合的根节点上,即修改==parent域==
void Union(int p,int q,TreeNode nodes[]){     //注意union是C关键字
	if(findRoot(p)!=findRoot(q))nodes[q].parent=p;
}

Union的优化

为了尽可能地降低union带来的树的高度增加的影响(合并后的==查找性能==),可以将规模(总节点个数)小的树合并到规模大的数上

将根节点的parent设置为该集合所有节点个数的==相反数==

另一种思路是将高度小的合并到高度大的树上

对集合的规模的维护:

  • 在合并的时候,合并后的集合需要将合并前的两个集合的规模数相加
void union(int p,int q,TreeNode nodes[]){
	int root1=findRoot(p);
	int root2=findRoot(q);
	if(nodes[root1].parent<nodes[root2].parent){   //因为都是负的所以是前者的绝对值大于后者
		nodes[root1].parent+=nodes[root2].parent;    //合并规模数
		nodes[root2].parent=root1;    //root2合并到root1上
	}
	else {
		nodes[root2].parent+=nodes[root1].parent;
		nodes[root1].parent=root2;
	}
}