選擇排序、插入排序、冒泡排序、希爾排序

簡介

以下總結幾個基礎的排序算法,包括選擇排序、插入排序、冒泡排序、希爾排序,這幾個排序算法是比較簡單的幾個。以下給出算法的分析和代碼示例。

時間複雜度

選擇排序、插入排序、冒泡排序、希爾排序四個排序算法的時間複雜度都是O(n^2)。

算法分析

選擇排序

選擇排序取第一個元素以此與後續的元素進行比較,保存最小的元素的下標,最終把最小的元素與第一個元素進行交換,第二次遍歷取第二個元素,在剩餘元素中選擇最小的元素與第二個元素交換,依次類推。。。。,最終實現把所有元素按照從小到大進行排序。

以下爲代碼示例

template<typename T>
void selectionSort(T arr[], int n){

    for(int i = 0 ; i < n ; i ++){

        int minIndex = i;
        for( int j = i + 1 ; j < n ; j ++ )
            if( arr[j] < arr[minIndex] )
                minIndex = j;

        swap( arr[i] , arr[minIndex] );
    }
}

選擇排序是基礎的排序算法,但需要重點理解,以後複雜的算法也都會以此爲基礎進行衍生。

 

插入排序

插入排序分爲兩種,第一種是直接插入排序,第二種是折半插入排序,以下分別描述兩種排序的基本思想:

直接插入排序的基本思想:當插入第i(i>1)個元素時,前面的data[0],data[1]……data[i-1]已經排好序。這時用data[i]的排序碼與data[i-1],data[i-2],……的排序碼順序進行比較,找到插入位置即將data[i]插入,原來位置上的元素向後順序移動。

折半插入排序的基本思想:設元素序列data[0],data[1],……data[n-1]。其中data[0],data[1],……data[i-1]是已經排好序的元素。在插入data[i]時,利用折半搜索法尋找data[i]的插入位置。

因爲插入排序每次循環比較可能提前退出,所以比選擇排序在性能上更優,特別是對於一些近乎有序的數據,插入排序的性能更優,插入排序對小數據量排序性能更好。

以下代碼爲直接插入排序的示例:

template<typename T>
void insertionSort(T arr[], int n)
{
	for(int i = 1; i < n; i++)
	{
		T tmp = arr[i];
		int j;
		for(j = i; j > 0 && arr[j - 1] > tmp; j --)
			arr[j] = arr[j - 1];

		arr[j] = tmp;
	}
}

 

冒泡排序

冒泡排序是進行兩兩比較,通過遍歷一遍所有的數據把最大的元素放到最後,然後繼續兩兩比較剩餘的數據,依次類推,直到所有的數據都有序。冒泡排序與插入排序類似,對於幾乎有序的數據,性能會更高。代碼如下:

template<typename T>
void bubbleSort(T arr[], int n)
{
  int newn;
  do
  {
    newn = 0;
    for(int i = 1; i < n; i++)
      if(arr[i - 1] > arr[i])
      {
        swap(arr[i - 1], arr[i]);

        //記錄最後一次交換的位置,在此之後的元素在下一輪比較中均不考慮
        newn = i;
      }
			
      n = newn;
   }while(newn > 0);
}

 

希爾排序

希爾排序的基本思想:

(1)希爾排序(shell sort)這個排序方法又稱爲縮小增量排序,是1959年D·L·Shell提出來的。該方法的基本思想是:設待排序元素序列有n個元素,首先取一個整數increment(小於n)作爲間隔將全部元素分爲increment個子序列,所有距離爲increment的元素放在同一個子序列中,在每一個子序列中分別實行直接插入排序。然後縮小間隔increment,重複上述子序列劃分和排序工作。直到最後取increment=1,將所有元素放在同一個子序列中排序爲止。

(2)由於開始時,increment的取值較大,每個子序列中的元素較少,排序速度較快,到排序後期increment取值逐漸變小,子序列中元素個數逐漸增多,但由於前面工作的基礎,大多數元素已經基本有序,所以排序速度仍然很快。

 

希爾排序的複雜度和增量序列是有關的

{1,2,4,8,...}這種序列並不是很好的增量序列,使用這個增量序列的時間複雜度(最壞情形)是O(n^2)

Hibbard提出了另一個增量序列{1,3,7,...,2^k-1},這種序列的時間複雜度(最壞情形)爲O(n^1.5)

Sedgewick提出了幾種增量序列,其最壞情形運行時間爲O(n^1.3),其中最好的一個序列是{1,5,19,41,109,...}

 

示例代碼如下:

template<typename T>
void InsertSort(T arr[], int h, int i)
{
	T tmp = arr[i]; 
	int j;
	for(j = i; j >= h && arr[j - h] > tmp; j -= h)
		arr[j] = arr[j - h];

	arr[j] = tmp;
}

template<typename T>
void shellSort(T arr[], int n)
{
	int h = 1;
	// 計算 increment sequence: 1, 4, 13, 40, 121, 364, 1093...
	while(h < n/3)
		h = 3 * h + 1;

	while(h >= 1)
	{
		//arr[h], arr[h+1], arr[h+2]....開始排序
		for(int i = h; i < n; i++)
		{
			//對arr[i], arr[i-h],...進行插入排序
			InsertSort(arr, h, i);
		}
		h /= 3;
	}
}

 

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