《算法第四版》学习:动态连通性

问题描述:给定了一组原始数据对,每个数据对<p, q>表示点p和点q相连。输入新的数据对<x,y>, 判断x,y是否已经在原始数据对中相连,如果不是,则输出x,y。

  这个问题有很多的变形,比如A认识B,A认识C,那么A是C的间接好友,如果有很多的这种人物关系当做原始数据集,再让判断新输入的两个人是否是间接好友,则就可以用这里的动态连接性。

最慢的实现方法: quick_find
import java.util.Scanner;
public class UnionFound
{
	private int[] id;  //用以存放每个节点的索引号
	private int count; // 连通分量的个数
	
	public UnionFound(int N)
	{
		count = N;
		id = new int[N];
		for(int ii=0; ii<N; ++ii)
			id[ii] = ii;
	}
	
	public int count()
	{
		return count;
	}
	
	//  quick_find 算法
	public int find(int p)
	{
		return id[p];
	}
	
	public void union(int p, int q)
	{
		int pId = id[p];
		int qId = id[q];
		
		if(pId == qId)
			return;
		
		for(int ii=0; ii<id.length; ++ii) //遍历所有的节点,将所有和p相同索引的节点都赋值为q.
		{
			if(pId == id[ii])
				id[ii] = qId;
		}
		count--;
	}
	
	public boolean connected(int p, int q)
	{
		  return find(p)==find(q);
 	}

	public static void main(String[] args)
	{
		Scanner scanner = new Scanner(System.in);
		int N = scanner.nextInt();
		UnionFound unionFound = new UnionFound(N);
		int[][] array = new int[][] {{4, 3}, {3, 8}, {6, 5}, {9, 4}, {2,1}, {8,9}, {5,0}, {7,2}, {6,1}, {1,0}, {6,7}};
		
		for(int ii=0; ii<array.length; ++ii)
		{
			int p = array[ii][0];
			int q = array[ii][1];
			if(unionFound.connected(p, q))
				continue;
			unionFound.union(p, q);
			StdOut.println(p + "  " + q);
		}
		
		while(scanner.hasNext())
		{
			int p = scanner.nextInt();
			int q = scanner.nextInt();
			if(unionFound.connected(p, q))
				continue;
			unionFound.union(p, q);
			StdOut.println(p + "  " + q);
		}
	}
}
quick_find的改进:quick_union

  该方法采用了树的数据结构,因此对于普通数据的计算量会减少很多,但是由于没有涉及树的合并方式,因此算法比较粗糙,在极端情况下(比如,1连2, 2-3, 3-4, …, N-1连N),算法的复杂度与quick_find相同。

import java.util.Scanner;
public class UnionFound
{
	private int[] id;
	private int count;
	
	public UnionFound(int N)
	{
		count = N;
		id = new int[N];
		for(int ii=0; ii<N; ++ii)
			id[ii] = ii;
	}
	
	public int count()
	{
		return count;
	}
	
	// quick_union算法
	public int find(int p)  //找到任意节点所对应的根节点
	{
		while(id[p] != p)  
			p = id[p];
		return p;
	}
	
	public void union(int p, int q)  // 将两棵树的根节点合并
	{
		int pRoot = find(p);
		int qRoot = find(q);
		if(pRoot == qRoot)
			return;
		id[pRoot] = qRoot;  
		count--;
	}
	
	public boolean connected(int p, int q)
	{
		  return find(p)==find(q);
 	}

	public static void main(String[] args)
	{
		Scanner scanner = new Scanner(System.in);
		int N = scanner.nextInt();
		UnionFound unionFound = new UnionFound(N);
		int[][] array = new int[][] {{4, 3}, {3, 8}, {6, 5}, {9, 4}, {2,1}, {8,9}, {5,0}, {7,2}, {6,1}, {1,0}, {6,7}};
		
		for(int ii=0; ii<array.length; ++ii)
		{
			int p = array[ii][0];
			int q = array[ii][1];
			if(unionFound.connected(p, q))
				continue;
			unionFound.union(p, q);
			StdOut.println(p + "  " + q);
		}
		
		while(scanner.hasNext())
		{
			int p = scanner.nextInt();
			int q = scanner.nextInt();
			if(unionFound.connected(p, q))
				continue;
			unionFound.union(p, q);
			StdOut.println(p + "  " + q);
		}
	}
}
稳定的对数复杂度:加权quick_union

  相对于常规的quick_union算法,加权quick_union算法先会判断待连接的两个树的大小,并总是将小的树连接到大的树上,因此,树的高度可以稳定在lgN。

public class WeightedQuickUnionFound
{
	private int[] id;	// 父链接数组
	private int[] sz;   // 各个根节点所对应的分量的大小
	private int count;	// 连通分量的数量
	
	public WeightedQuickUnionFound(int N)
	{
		count = N;
		id = new int[N];
		sz = new int[N];
		for(int ii=0; ii<N; ++ii)
		{
			id[ii] = ii;
			sz[ii] = 1;
		}
	}
	
	public int find(int p)
	{
		while(id[p] != p)
			p = id[p];
		return p;
	}
	
	public void union(int p, int q)
	{
		int pRoot = find(p);
		int qRoot = find(q);
		if(pRoot == qRoot)
			return;
		if(sz[pRoot] <= sz[qRoot])
		{
			id[pRoot] = qRoot;
			sz[qRoot] += sz[pRoot];
		}
		else
		{
			id[qRoot] = pRoot;
			sz[pRoot] += qRoot;
		}
		count--;
	}
	
	public boolean connected(int p, int q)
	{
		  return find(p)==find(q);
 	}
	
	public int count()
	{
		return count;
	}
	
	public static void main(String[] args)
	{
		int[][] array = new int[][] {{4, 3}, {3, 8}, {6, 5}, {9, 4}, {2,1}, {8,9}, {5,0}, {7,2}, {6,1}, {1,0}, {6,7}};
		WeightedQuickUnionFound unionFound = new WeightedQuickUnionFound(array.length);
		
		for(int ii=0; ii<array.length; ++ii)
		{
			int p = array[ii][0];
			int q = array[ii][1];
			if(unionFound.connected(p, q))
				continue;
			unionFound.union(p, q);
			StdOut.println(p + "  " + q);
		}
		
	}
}
  如果想要继续了解动态连通性相关算法,请查阅《算法》(第四版),第1.5小节。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值