幾種常見排序算法
1冒泡排序(BubbleSort)
冒泡排序思路:
1. 將序列當中的左右元素,依次比較,保證右邊的元素始終大於左邊的元素;(第一輪結束後,序列最後一個元素一定是當前序列的最大值;)
2. 對序列當中剩下的n-1個元素再次執行步驟1。
3. 對於長度爲n的序列,一共需要執行n-1輪比較
代碼實現:
for (i= 0; i < n; i++)
{
for (j = 0; j < n - i; j++)
if (a[j] > a[j+1])
{
t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
冒泡排序是穩定的。算法時間複雜度是O(n^2),空間複雜度O(1).
2直接選擇排序(SelectionSort)
直接選擇排序的基本思想:比較+交換。
1. 從待排序序列中,找到關鍵字最小的元素;
2. 如果最小元素不是待排序序列的第一個元素,將其和第一個元素互換;
3. 從餘下的 N - 1 個元素中,找出關鍵字最小的元素,重複(1)、(2)步,直到排序結束。
選擇排序通過兩層循環實現。
第一層循環:依次遍歷序列當中的每一個元素
第二層循環:將遍歷得到的當前元素依次與餘下的元素進行比較,符合最小元素的條件,則交換。
直接選擇排序是不穩定的:算法的時間複雜度爲O(n^2),空間複雜度是O(1).
代碼實現:
def select_sort(L):
#依次遍歷序列中的每一個元素
for x inrange(0,len(L)):
#將當前位置的元素定義此輪循環當中的最小值
minimum =L[x]
#將該元素與剩下的元素依次比較尋找最小元素
for i inrange(x+1,len(L)):
if L[i]< minimum:
temp = L[i];
L[i] = minimum;
minimum = temp
#將比較後得到的真正的最小值賦值給當前位置
L[x] =minimum
3直接插入排序(InsertionSort)
直接插入排序的核心思想就是:將數組中的所有元素依次跟前面已經排好的元素相比較,如果選擇的元素比已排序的元素小,則交換,直到全部元素都比較過。
直接插入排序是穩定的。算法時間複雜度是O(n^2),空間複雜度是O(1).
直接插入排序可以用兩個循環完成:
1. 第一層循環:遍歷待比較的所有數組元素
2. 第二層循環:將本輪選擇的元素(selected)與已經排好序的元素(ordered)相比較。
如果:selected > ordered,那麼將二者交換
代碼實現:
definsert_sort(L):
#遍歷數組中的所有元素,其中0號索引元素默認已排序,因此從1開始
for x inrange(1,len(L)):
#將該元素與已排序好的前序數組依次比較,如果該元素小,則交換
#range(x-1,-1,-1):從x-1倒序循環到0
for i inrange(x-1,-1,-1):
#判斷:如果符合條件則交換
ifL[i] > L[i+1]:
temp = L[i+1]
L[i+1] = L[i]
L[i] = temp
4堆排序
堆排序是一種樹形選擇排序,在排序過程中,將A[n]看成是完全二叉樹的順序存儲結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關係來選擇最小的元素。
堆排序是不穩定的。算法時間複雜度O(nlog n),空間複雜度是O(1).
1.首先將序列構建稱爲大頂堆;
2.取出當前大頂堆的根節點,將其與序列末尾元素進行交換;
3.對交換後的n-1個序列元素進行調整,使其滿足大頂堆的性質;
4.重複2.3步驟,直至堆中只有1個元素爲止
代碼實現:
void HeapSort(RecordType r[],int length)
{ crt_heap( r, length);n= length;
for ( i=n ; i>= 2 ; --i)
{
b=r[1]; /* 將堆頂記錄和堆中的最後一個記錄互換 */
r[1]= r[i] r[i]=b;
sift(r,1,i-1) ; /* 進行調整,使r[1..i-1]變成堆 */
}
} /* HeapSort */
5歸併排序
採用分治法算法,歸併排序實際上就是兩個操作,拆分+合併,
分解----將序列每次折半拆分
合併----將劃分後的序列段兩兩排序合併
歸併排序是穩定的:其時間複雜度無論是在最好情況下還是在最壞情況下均是O(nlog2n), 空間複雜度是O(1).
合併算法實現:
void Merge ( RecordType r1[], int low, int mid, int high, RecordType r[])
{
i=low;j=mid+1; k=low;
while ( (i<=mid)&&(j<=high))
{if ( r1[i].key<=r1[j].key )
{
r[k]=r1[i] ;
++i;
}
else
{
r[k]=r1[j] ;
++j;
}
++k ;
}
if ( i<=mid )
r[k..high] =r1[i..mid];
if ( j<=high )
r[k..high] =r1[j..high];
}
6快速排序
快速排序是對冒泡排序的一種本質改進。
它的基本思想(挖坑填數+分治法)是通過一趟掃描後,使得排序序列的長度能大幅度地減少。在冒泡排序中,一次掃描只能確保最大數值的數移到正確位置,而待排序序列的長度可能只減少1。快速排序通過一趟掃描,就能確保某個數(以它爲基準點吧)的左邊各數都比它小,右邊各數都比它大。然後又用同樣的方法處理它左右兩邊的數,直到基準點的左右只有一個元素爲止。
用僞代碼描述如下:
1.i =L; j = R; 將基準數挖出形成第一個坑a[i]。
2.j--由後向前找比它小的數,找到後挖出此數填前一個坑a[i]中。
3.i++由前向後找比它大的數,找到後也挖出此數填到前一個坑a[j]中。
4.再重複執行2,3二步,直到i==j,將基準數填入a[i]中
快速排序是不穩定的。最理想情況算法時間複雜度O(nlog2n),最壞O(n ^2),空間複雜度爲O(nlog2n)。
代碼實現:
int quicksort(vector<int> &v, int left, int right){ if(left < right){ int key = v[left]; int low = left; int high = right; while(low < high){ while(low < high && v[high] > key){ high--; } v[low] = v[high]; while(low < high && v[low] < key){ low++; } v[high] = v[low]; } v[low] = key; quicksort(v,left,low-1); quicksort(v,low+1,right); } }
7希爾排序 (shell)
在直接插入排序算法中,每次插入一個數,使有序序列只增加1個節點,並且對插入下一個數沒有提供任何幫助。如果比較相隔較遠距離(稱爲增量)的數,使得數移動時能跨過多個元素,則進行一次比較就可能消除多個元素交換。
希爾排序的算法思想:將待排序數組按照步長gap進行分組,然後將每組的元素利用直接插入排序的方法進行排序;每次將gap折半減小,循環上述操作;當gap=1時,利用直接插入,完成排序。
希爾排序是穩定的:算法時間複雜度是O(n^2),空間複雜度是O(1).
希爾排序的總體實現應該由三個循環完成:
1. 第一層循環:將gap依次折半,對序列進行分組,直到gap=1
2. 第二、三層循環:也即直接插入排序所需要的兩次循環。具體描述見上。
代碼實現:
def insert_shell(L):
#初始化gap值,此處利用序列長度的一般爲其賦值
gap = (int)(len(L)/2)
#第一層循環:依次改變gap值對列表進行分組
while (gap >= 1):
#下面:利用直接插入排序的思想對分組數據進行排序
#range(gap,len(L)):從gap開始
for x in range(gap,len(L)):
#range(x-gap,-1,-gap):從x-gap開始與選定元素開始倒序比較,每個比較元素之間間隔gap
for i in range(x-gap,-1,-gap):
#如果該組當中兩個元素滿足交換條件,則進行交換
if L[i] > L[i+gap]:
temp = L[i+gap]
L[i+gap] = L[i]
L[i] =temp
#while循環條件折半
gap = (int)(gap/2)