排序算法(3) ---交換排序

  應用交換排序基本思想的主要排序方法有:冒泡排序和快速排序。這裏將主要介紹快速排序,以及快速排序的遞歸與非遞歸的實現和對快速排序的優化

冒泡排序

     冒泡排序是將大數或小數不斷後移的一種思想,比較和交換都發生在兩個相鄰元素之間。
    思想:
        1.單趟排序:比較相鄰的元素,如果第一個比第二個大(小),交換這兩個元素,直到最後元素,則最後元素的值應該爲最大或最小值,前面的元素可能有序也可能無序。
        2.對前面元素在進行單趟排序,改變每趟排序的下標,進行單趟排序,直到沒有一對數據進行比較時,則排序完成。
    優化:
       對於冒泡排序,經過單趟排序後,前面的元素可能有序,若前面元素已經有序,則可終止排序,提高效率。(設置標誌flag=false,如果在每次單趟排序有數據交換,則將flag=true,, 每次單趟排序完可對flag進行判斷,若flag==false,說明前面元素已經有序,直接跳出循環,否則,繼續進行冒泡。)
    圖解:

   實現:
void BubbleSort(int* a, size_t n)
{
	assert(n);
	bool finish = false; //堆排序進行優化
	size_t end = n;
	while(end > 0) //end表示每次冒泡的終止位置
	{
		//單趟冒泡
		for(size_t i=1; i<end; i++)
		{
			if(a[i-1]>a[i])
			{
				swap(a[i-1],a[i]);
				finish = true;  //若單趟排序有數據交換,則將finish置爲true
			}
		}

		if(finish == false)  //判斷單趟排序中是否有交換
		{
			return;
		}

		--end;
	}
}

    算法分析:
         算法時間複雜爲O(n^2),屬於穩定性算法

快速排序


快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列
   圖解: 
                              

        分析:快速排序應用分治法的思想,將大問題分解爲小問題,先求解子問題,以得到對原問題的求解。
        遞歸實現:            
//左右指針法	找一個key,將大數右移,小數左移,最後將數組區間劃分爲兩部分 [] key []
int PartSort1(int* a, int begin, int end)
{
	int left = begin, right = end; //right賦值爲 end 還是 end-1
	int key = a[right]; //選擇最右邊爲key值
	//單趟排序
	while(begin < end)
	{
		//begin找大於
		while(begin < end && a[begin] <= key) //注意條件a[begin] <= key 
		{
			++begin;
		}

		//end找小
		while(begin < end && a[end] >= key)
		{
			--end;
		}

		swap(a[begin], a[end]);
	}
	swap(a[begin],a[right]); //a[right]相當於中間劃分的位置

	return begin; 
}

void QuickSort(int* a, int left, int right)
{
	assert(a);

	if(left >= right) //注意:結束條件的判斷
	{
		return;
	}

	int div = PartSort1(a, left, right);

	QuickSort(a, left,div-1);
	QuickSort(a, div+1, right); //right爲什麼不-1;
}
算法分析
       
 最壞情況:
        如果我們在選取基準p的時候,每次選取的都是當前數組中最小的一個元素,那麼每次劃分都只是讓數組中的元素少1(被篩選出來的那個元素當然有序),這樣一來就需要反覆遍歷數組導致複雜度變成了O(n2)。
   最好情況:
     如果我們在選取基準p的時候,每次選取的都是當前數組中最中間的一個元素(是中位數,而不是元素位置上的中間),那麼每次劃分都把當前數組劃分成了長度相等的兩個子數組,這樣一來複雜度變成了O(nlog2n)。
基準的選擇
1.對於基準元素的選取,也可以採用隨機數選取的方式
2.如果要按照元素在位置上的中間來選取基準元素,還可以將中間位置上的元素與第一個元素進行對換
優化:
三平均分區法
這一改進與其它的快速排序方法不同,它並不是選擇待排數組的第一個元素作爲中軸,而是選用待排數組最左邊、最右邊和最中間的三個元素的中間值作爲中軸。這一改進的優勢:
(1)首先,它使得最壞情況發生的機率減小了。
(2)其次,未改進的快速排序算法爲了防止比較時數組越界,在最後要設置一個哨點。

非遞歸實現快排:
藉助棧,棧中保存左右下標,先壓右、在壓左,棧爲空時循環結束
注:壓棧應注意左右下標有意義時才壓棧及(left<right).
void QuickSortFD(int* a, int left, int right)
{
	stack<int> s; //棧中存放下標
	if(left < right) //首先要保證下標有意義再壓棧
	{
		s.push(right);
		s.push(left);
	}

	while(s.size()>0)
	{
		
		int left = s.top();
		s.pop();
		int right = s.top();
		s.pop();
		if(right - left <= 20)
		{
			InsertSort(a+left,right-left+1);
			return;
		}
		else
		{
			int div = PartSort1(a,left,right);

			if(div - 1 >left) //區間不合法時,不入棧
			{
				s.push(div-1);
				s.push(left);
			}

			if(right > div+1)
			{
				s.push(right);
				s.push(left);
			}
		}
	}
}

    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章