演算法學習-荷蘭國旗問題

HelloNiGeSB發表於2016-12-06

題目

現有紅、白、藍三個不同顏色的小球,亂序排列在一起,請重新排列這些小球,使得紅白藍三色的同顏色的球在一起。

問題分析

問題轉換為:給定陣列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時直接跳過,利於資料規模的降低





相關文章