【排序】插入排序,希爾排序,選擇排序,冒泡排序,堆排序,歸併排序優化及穩定性分析

InsertSort:(以升序爲例)

插入排序的基本思想是:

    每次將一個待排的記錄,按照其關鍵字的大小,插入到前面已經排好序的有序區中適當的位置,直到全部記錄插入完畢爲止。

    假設待排序的記錄存放在數組R[0..n]中,初始時R[0]是一個有序區,R[1..n]是無序區,從i=1開始,依次將R[i]插入到有序區R[0..i-1]中,生成一個包含n個記錄的有序區

void InsertSort(int *arr, int len)
{
	int i = 0;
	int j = 0;
	int tmp = 0;
	for (i = 1; i < len; i++)
	{
		tmp = arr[i];
		for (j = i; j>0 && arr[j - 1]>tmp; j--)       //在有序區找到一個正確的位置
		{
			arr[j] = arr[j - 1];                  //將當前記錄後移
		}
		arr[j] = tmp;                             //將tmp插入到找到的位置
	}
}

優化:折半插入

既然有序區已經有序,那麼我們在尋找合適位置的時候,可以使用折半查找的思想在有序區尋找這個位置。

穩定性

  直接插入排序是穩定排序,即相同的數據元素在排序前後不會改變二者之間的相對順序。

希爾排序

  希爾排序又叫縮小增量排序,其實希爾排序就是插入排序的一種,只不過對插入排序做了優化。 
  在插入排序中,我們每次相當於每隔一個的再插入,希爾排序的思想是:

設待排序元素有n個,首先取一個整數gap<n作爲間隔,將全部元素分爲gap組,所有距離爲gap的元素都在同一個組裏,然後對每一個組都進行插入排序。然後縮小增量,直到gap==1的時候整個序列都會變成有序的。

希爾排序與插入排序的比較:

       插入排序在什麼情況下效率最高呢???

       如果當要排序的序列大致上接近於有序的時候,插入排序的效率最高,基本上接近O(N)。因爲當數據基本上接近於有序的時候,元素向有序區插入的時候比較的次數會很少,同樣有序區元素向後移動的次數也是很少的。

如果當一組數據接近的有序的話,我們優先使用插入排序,效率高。如果當一組數據接近於逆序的話,我們使用希爾排序會更優。插入情況最好的情況就是希爾排序最壞的情況,因爲這時候希爾排序的預排序起不到作用。插入排序最壞的情況則是希爾排序最高的情況,因爲這時候預排序的效果是非常明顯的。

穩定性

  希爾排序是不穩定的排序,因爲我們將原序列分組了,不能保證兩個相同的數據在同一組,所以導致其是不穩定排序。

選擇排序

選擇排序的基本思想:初始時,有序區爲0,每一趟在無序區中選出一個關鍵字最小的元素,然後與無序區第一個元素交換,然後無序區減1,有序區加1,直到記錄全部排完

優化

  既然每一次我們都會遍歷待排序序列,那何嘗不同時將最大值,最小值都找出來,分別放入待排序序列的頭和尾呢?

穩定性

   選擇排序是不穩定的排序方法。

冒泡排序

         它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。 

冒泡排序的優化

           我們已經知道當走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成,所以我們可以設置一個標誌位來判斷是否已經排序完畢。

void BubbleSort(int *arr, int len)
{
	assert(arr);
	int i = 0;
	int j = 0;
	int flag = 0;
	int tmp = 0;
	for (i = 0; i < len - 1; i++)
	{
		flag = 1;                              //flag初始時爲1
		for (j = 0; j < len - i - 1; j++)       //每排序一趟,則必然後面有一個已經有序,可以縮小排序的範圍
		{
			if (arr[j]>arr[j + 1])             //只要要交換數據,則flag就會被修改
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;                    //只要這組數還未完全有序,則一定會修改flag爲0
			}
		}
		if (flag)                             //如果排序一趟,發現已經有序,則不進入if,flag沒被修改
		{
			break;
		}
	}
}

穩定性

  冒泡排序就是把小的元素往前調或者把大的元素往後調。比較是相鄰的兩個元素比較,交換也發生在這兩個元素之間。所以,如果兩個相等的元素沒有相鄰,那麼即使通過前面的兩兩交換把兩個相鄰起來,這時候也不會交換,所以相同元素的前後順序並沒有改變,所以冒泡排序是一種穩定排序算法。

堆排序

        是用數組模擬樹的形式進行的排序。但是堆數據結構只保證父節點的值大於或者小於子節點的值,並不保證整體的順序。但是我們可以利用根節點的值永遠是最大,或者最小的這個特性,利用根節點找出最值,再與尾位置互換,即可排序成功。 

穩定性

  因爲每次根節點的值都是不確定的,所以我們也不能保證相同元素的父節點是相同的,所以堆排序是不穩定的

歸併排序

    是一種基於分治法的一種排序方法。它將要排序的序列分成兩個長度相等的子序列,爲每一個子序列進行排序,然後再將子序列合併成一個有序的序列。

優化:

  當劃分的子區間的元素小於13個元素左右時,再使用歸併的話會效率不會很高,相當於多增加了二叉樹的後面幾層的結點。這時候我們改用直接插入排序來進行優化

穩定性

歸併排序是把序列遞歸地分成短序列,遞歸出口是短序列只有1個元素(認爲直接有序),然後把各個有序的段序列合併成一個有 序的長序列,是穩定排序。

 

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