三色旗算法

三色旗的问题最早由Dijkstra所提出,他称该问题为Dutch Nation Flag(Dijkstra为荷兰人),然而多数的作者则使用Three-Color Flag来称该问题。

问题描述:假设有一条绳子,上面有红、白、蓝三种颜色的旗子,起初绳子上的旗子颜色并没有顺序,您希望将之分类,并排列为蓝、白、红的顺序,要如何移动次数才会最少,注意您只能在绳子上进行这个动作,而且一次只能调换两个旗子。

问题分析:该问题中标蓝的点都需要我们在解决该问题的时候予以注意。下面,我们针对上述需要注意的点进行分析。

① 移动次数最少:如果没有此项限制,我们可以使用归并、快排、冒泡等算法,实现三色排序。但是上述排序算法需要对绳子上的所有旗子进行排序,然而相同颜色的旗子可以不用排序,所以上述算法达不到题目中要求移动次数最少这一要求。计数排序的话,只需要一次遍历既可以实现稳定排序,所以计数排序有可能解决上述问题;

② 只能在绳子上:在这项条件限制下,计算排序则不满足条件,这是因为计数排序需要新申请空间,在新申请空间中排序,不满足题目中只能有一条绳子的要求。

解决该问题算法可能应该具有的特点:原地排序、线性遍历、应该像hash函数,遇到什么数扔到什么桶中

解决方案:在上述特点的指引下,我们发现快排中partition算法具有成为解决方案的潜质。在快排的partition算法中,只是使用了两种状态的信息,即“大于”或者“小于”。而为了解决该问题我们需要利用三色状态的信息。类比partition算法的思想,我们可以写出该算法的伪代码:

我们假设a数组中存储旗子信息,N为旗子总数,下标从1开始。
1. 红色旗子区域:a[1..Lo-1] zeroes (red)
2. 白色旗子区域:a[Lo..Mid-] ones (white)
3.     未知区域:a[Mid..Hi] unknown
4. 蓝色旗子区域:a[Hi+1..N] twos (blue)

//
//荷兰国旗算法或者三路划分
//
///
// 在程序尚未开始运行时,我们假设红色旗子区域[1...1-1]、
// 白色旗子区域[1...1-1]和蓝色旗子区域[N+1...N]为空。
// 整个区域为未知区域。
Lo := 1; Mid := 1; Hi := N;
// 遍历整个数组交换错位的旗子
while Mid <= Hi do
	// 循环不变量是程序运行时需要维持不变,在已有的基础之上,再加入新的东西
	// 按照不同旗子的颜色,将不同的区域划分为桶(红色、白色以及蓝色),用不同的颜色代表不同的桶,用不同的颜色代表不同的桶
	Invariant: a[1..Lo-1]=0 and a[Lo..Mid-1]=1 and a[Hi+1..N]=2; a[Mid..Hi] are unknown.
	//判断旗子的颜色,放入不同的桶中,并且需要动态地调整桶的大小
	switch a[Mid] in
		case 0: swap a[Lo] with a[Mid]; ++Lo; ++Mid; //0,1桶的大小分别增加了1
	        case 1: ++Mid; //1 桶的大小增加了1
		case 2: swap a[Mid] with a[Hi]; --Hi; //2 桶的大小增加了1,然而换过之后1桶是否增加并不清楚,所以尚需进一步判断


例子:下面举一个例子,说明该算法是如何工作的。假设该算法已经运行了几步,部分红色、百色以及蓝色已经在指定的位置,只有a[Mid...Hi]是位置,通过检查该未知区域的数据,将正确的数据放置到指定的位置,如下图所示:



检查a[Mid],判断该旗子应该放置到哪一个桶中。有三种可能性:a[Mid]=0 为红色旗子;a[Mid]=1为白色旗子;a[Mid]=2为蓝色旗子。

第一种情况:当a[Mid]=0 为红色旗子时,需要将该旗子放到前面红色旗子的桶中,并且相应地调整红色旗子和白色旗子桶的大小和起始位置




第二种情况:当a[Mid]=1 为白色旗子时,继续移动,表示白色旗子的桶在不断变大。




第三种情况,当a[Mid]=2 为蓝色旗子时,需要将该旗子放到后面蓝色旗子的桶中,并且相应地调整蓝色旗子大小和起始位置



N旗子的个数

时间复杂度分析:O(N)

空间复杂度分析:O(1)

注:本文在主要参考文献的思路指导下,加入了作者的一些理解。

参考文献:

http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Flag/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值