1. 程式人生 > >氣泡排序的三種優化

氣泡排序的三種優化

傳統的氣泡排序完全可以滿足我們最基本的需求,但是也僅僅是最簡單的需求,這種簡單的兩個for迴圈不加任何的判斷語句的形式註定它只能是一種效率最低的演算法。

我們先貼一個傳統的實現方式,之後的三個優化全部建立在函式排序所使用的消耗上,這也是我們優化一切演算法的根本路徑。

void BubbleSort(int* arr,int size)
{
	assert(arr&&size);
	if(size==1)
		return;
	for(int i=0;i<size-1;i++)
	{
		for(int j=0;j<size-1-i;j++)
		{
			if(arr[j]>arr[j+1])
			{
				int tmp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=tmp;
			}	
		}
	}
}

這種傳統的排序次數是固定的,即給出相同數目的幾個陣列 不論其元素排列是怎麼樣,時間複雜度都為O(N^2)。尤其當我們遇到下面這種序列

即: 1,2,3,5,4  我們只需要排一趟就可以了,基於這種情況我們給出了下面這種優化

void BubbleSort_Optimize_1(int* a,int size)
{
	assert(a);
	if(size==1)
		return;

	bool flag=0;//定義標誌位標記已經有序或無序
	for(int i=0;i<size-1;i++)
	{
		flag=1;//開始置為1
		for(int j=0;j<size-1-i;j++)
		{
			if(a[j]>a[j+1])
			{
				int tmp=a[j];
				a[j]=a[j+1];
				a[j+1]=tmp;
				flag=0;//交換後對flag置0,表示已經有序
			}
		}
		if(flag)
			break;//如果flag為1則說明排序前已經有序
	}
}

然而這種優化只能做到某一次已經排好序的時候我們直接跳跳出來,基於第一種優化我們得到一種啟發:當一個數組接近有序的時候我們往往只需要在某一個小範圍內排序即可,我們可以用一個標記來表示這個範圍的下限,例如遇到下面的情況

然而我們發現,每次排序前或排序後陣列的後面都有一部分已經有序,這時我們只要記下最後一次排下的陣列的下標下次排序的時候就可以只排序到此下標位置即可

第二個優化版本

void BubbleSort_Optimize_2(int* a,int size)
{
	assert(a);
	if(size==1)
		return;

	bool flag=0;//定義標誌位標記已經有序或無序
	int index=size-1;
	int max_index=0;//每一次我們找到無序區的上界
	for(int i=0;i<size-1;i++)
	{
		flag=1;//開始置為1
		max_index=0;//這裡也可以不寫
		for(int j=0;j<index;j++)
		{
 			if(a[j]>a[j+1])
			{
				int tmp=a[j];
				a[j]=a[j+1];
				a[j+1]=tmp;
				flag=0;//交換後對flag置0,表示已經有序
				max_index=j;//注意不要在這裡直接將index置為j
			}
		}
		if(flag)
			break;//如果flag為1則說明排序前已經有序
		index=max_index;//若排序過則將index置為最後一次交換的座標,若沒有則表示已經有序
	}
}
這裡的第三種優化是在前兩種優化的基礎上借用了類似於選擇排序的思想(有沒有發現選擇排序和氣泡排序的程式碼有點相似),但是我們進行的是正反交替掃描,正著掃描得到最大值反著掃描得到最小值(或者顛倒順序),這樣做的目的是為了當陣列本身已經接近有序或部分有序的時候多餘的判斷,這樣我們每次得到無序區的最大值和最小值只對它們排序就可以了。
void BubbleSort_Optimize_3(int* a,int size)
{
	assert(a);
	if(size==1)
		return;
	
	int max_index=0;//每一次排序我們得到無序區的上界
	int min_index=0;//每一次排序我們得到無序區的下界
	int index=size-1;
	bool flag=0;//定義標誌位標記已經有序或無序

	for(int i=0;i<size-1;i++)
	{
		flag=1;//開始置為1
		min_index=0;//這裡也可以不寫
		for(int j=0;j<index;j++)
		{
			//正序掃描找到最大值
 			if(a[j]>a[j+1])
			{
				int tmp=a[j];
				a[j]=a[j+1];
				a[j+1]=tmp;
				flag=0;//交換後對flag置0,表示已經有序
				max_index=j;//注意不要在這裡直接將max_index置為j
			}
		}
		if(flag)
			break;//如果flag為1則說明排序前已經有序
		index=max_index;//若排序過則將index置為最後一次交換的座標,若沒有則表示已經有序

		for(int j=index;j>min_index;j--)
		{
			//逆序掃描找到最小值
 			if(a[j]<a[j-1])
			{
				int tmp=a[j];
				a[j]=a[j-1];
				a[j-1]=tmp;
				flag=0;//交換後對flag置0,表示已經有序
			}
		}
		min_index++;
		if(flag)
			break;//如果flag為1則說明排序前已經有序
	}
}