算法学习-荷兰国旗问题

本文介绍了一种名为荷兰国旗问题的经典算法挑战,通过三种不同版本的实现详细解释了如何对一个只包含0、1、2三个数值的数组进行排序,使其形成连续的相同数值序列。此外,还探讨了该问题与快速排序的关系及其潜在的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

现有红、白、蓝三个不同颜色的小球,乱序排列在一起,请重新排列这些小球,使得红白蓝三色的同颜色的球在一起。

问题分析

问题转换为:给定数组A[0...N-1],元素只能取0、1、2三个值,设计算法,使得数组排列成“00...0011...1122...22”的形式

借鉴快速排序中partition的过程,定义三个指针begin=0,current=0,end=N-1

A[cur]==2,则A[cur]与A[end]交换,end--,cur不变

A[cur]==1,则cur++,begin不变,end不变

A[cur]==0,则:

若begin==cur,则begin++,cur++

若begin!=cur,则A[cur]与A[begin]交换,begin++,cur不变


代码如下

void Holland1(int* a, int length)
{
	int begin = 0;
	int current = 0;
	int end = length - 1;
	while (current <= end)
	{
		if (a[current] == 2)
		{
			swap(a[end], a[current]);
			end--;
		}
		else if (a[current] == 1)
		{
			current++;
		}
		else
		{
			if (begin == current)
			{
				begin++;
				current++;
			}
			else
			{
				swap(a[current], a[begin]);
				begin++;
			}
		}
	}
}


第二个版本:

cur扫过的位置,即:[begin,cur)区间内,一定没有2

在前面的A[cur]==2中,已经被替换到数组后面了

因此:A[begin]要么是0,要么是1,不可能是2

考察begin指向的元素的值:

归纳法:若begin!=cur,则必有A[begin]=1

因此,当A[cur]==0时,

若begin==cur,则begin++,cur++;

若begin!=cur,因为A[begin]==1,则交换后,A[cur]==1,此时,可以cur++;

void Holland2(int* a, int length)
{
	int begin = 0;
	int current = 0;
	int end = length - 1;
	while (current <= end)
	{
		if (a[current] == 2)
		{
			swap(a[end], a[current]);
			end--;
		}
		else if (a[current] == 1)
		{
			current++;
		}
		else
		{
			if (begin == current)
			{
				begin++;
				current++;
			}
			else
			{
				swap(a[current], a[begin]);
				begin++;
				current++;
			}
		}
	}
}



终极版本

void Holland(int* a, int length)
{
	int begin = 0;
	int current = 0;
	int end = length - 1;
	while (current <= end)
	{
		if (a[current] == 2)
		{
			swap(a[end], a[current]);
			end--;
		}
		else if (a[current] == 1)
		{
			current++;
		}
		else
		{
			if (current != begin)
			{
				swap(a[current], a[begin]);
			}
			begin++;
			current++;
		}
	}
}

荷兰国旗问题扩展

  1. 将0/1/2分别计数,根据三个计数值c0/c1/c2:前c0个元素赋值为0,中间c1个元素赋值为1,最后c2个元素赋值为2;实际意义比较小,可能排序结构比较复杂的时候就用着不方便了
  2. 将(0,1)(2)根据快速排序的Partition,划分为两部分(如PivotKey1.5);将(0)(1)根据快速排序Partition,分成两部分(如PivotKey选择0.5);那么可以得到结论“两次Partition==一次荷兰国旗”,这样可以优化快速排序的Partition过程

优化快速排序根据PivotKey分成大于、小于等于两部分或者大于等于、小于两部分

根据PivotKey的大小,将Partition过程盖在成大于、等于、小于三部分

优点:对于快速排序的等于PivotKey的数值,可以在执行下一次Partition时直接跳过,利于数据规模的降低





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值