常見的排序算法有以下7種:
- 冒泡排序
- 選擇排序
- 插入排序
- 堆排序
- 希爾排序
- 歸併排序
- 快速排序
我們通常說的排序算法往往指的是內部排序算法,即數據記錄在內存中進行排序
- 排序算法大體可分爲兩種:
1.一種是比較排序,時間複雜度爲O(nlogn)~O(n^2),主要有冒泡排序,選擇排序,插入排序,歸併排序,堆排序,快速排序等
2.另一種是非比較排序,時間複雜度可以達到O(n)
除了排序算法的時間複雜度是我們比較關心的,還有一個就是排序算法的穩定性。
排序算法穩定性的簡單形式化定義爲:如果Ai = Aj,排序前Ai在Aj之前,排序後Ai還在Aj之前,則稱這種排序算法是穩定的。
接下來先介紹4種排序算法:
1.冒泡排序(這裏可以從前向後冒泡,也可以從後向前冒泡)
(1)比較兩個相鄰元素,如果前一個比後一個大,就將兩個元素進行交換
(2)依次向後進行比較操作,一趟冒泡下來,最後一個元素爲當前這組數中的最大值
(3)繼續再進行如上操作,進行第二次冒泡,但是最後一個元素不用進行比較,因爲已經有序,以此類推第三次冒泡時,最後兩個元素不需要比較
(4)重複上述操作,進行 元素個數-1 次冒泡,排序完成
如圖是對一組數進行一趟冒泡排序的結果,按照此方法繼續進行冒泡,就可以得到排序結果
下面是代碼實現:
void BubbleSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//[0,i)表示有序區間
//[i,size)表示待排序區間
size_t i = 0;
for(;i < size ;++i)
{
//這裏採取的是從後向前冒泡
size_t j = size -1;
for(;j > i;--j)
{
if(array[j] < array[j-1] )
{
Swap(&array[j],&array[j-1]);
}
}
}
return;
}
2.選擇排序
(1)第一次排序,第一個元素和後面的元素依次比較大小,如果第一個元素大的話就交換兩個元素的位置,保證一次排序後第一個元素爲這組數的最小值
(2)第二次排序,第二個元素和後面的元素依次比較大小,如果第二個元素大的話就交換兩個元素的位置,保證第二次排序後第二個元素爲剩下數中的最小值
(3)重複上述操作,就可以將該組數進行排序
如圖是進行一次選擇排序的結果,按照此方法繼續進行選擇排序就可以得到排序結果
下面是代碼實現:
void SelectSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//[0,i)表示有序區間
//[i,size)表示待排序區間
size_t i = 0;
for(; i < size ;++i)
{
size_t j = i + 1;
for(; j < size;++j)
{
if(array[i] > array[j])
{
Swap(&array[i],&array[j]);
}
}
}
return;
}
3.插入排序
(1)從第一個元素,該元素可以被認爲已經有序
(2)取出下一個元素,先保存起來,在已經排序的元素序列中從後向前比較
(3)如果當前元素大於要插入的元素,就把當前元素向後移
(4)重複步驟3,直到找到已排序的元素小於或等於新元素的位置
(5)將新元素插入到該位置
(6)重複上述操作,即可完成排序
如圖是進行插入排序的過程
下面是代碼實現:
void InsertSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//[0,i)表示有序區間
//[i,size)表示待排序區間
//插入排序是把前面的有序區間當做一個線性表
//然後把 i_value 的值插入到線性表中合適的位置上
size_t i = 1;
for(; i < size;++i)
{
//此時存起來的意義是爲了後面的搬運
//一旦array[i]元素被單獨保存起來了
//array[i]數值就可以被修改了
int i_value = array[i];
//此時cur是輔助我們進行搬運的下標
//從後向前遍歷,找到合適的位置放 i_value 的位置
size_t cur = i;
for(;cur > 0;--cur)
{
//此處我們的初始情況就是拿線性表的最後一個元素和 i_value 比較
//因此 array[cur-1]這裏的 cur 取決於 cur 的初始位置
if(array[cur-1] > i_value)
{
//進行搬運
array[cur] = array[cur-1];
}
else
{
//說明已經找到了合適的位置
break;
}
}
//然後把i位置的元素插入到線性表的合適的位置上
array[cur] = i_value;
}
return;
}
4.堆排序
(1)基於數組建立一個堆(如果是升序就建立大堆)
(2)循環的刪除堆頂元素,將所有的元素都刪除完畢,排序完成
(3)每次刪除堆頂元素後,都對堆進行調整,這裏有兩種方法
方法一:把新元素放到數組的末尾,進行上浮式調整(從前往後遍歷)
方法二:採用下沉式的調整(從後往前遍歷)
起始位置就是堆的從後往前遍歷的第一個非葉子節點
如上給定一個數組建立一個堆已經完成,剩下的就是對堆進行刪除堆頂元素,然後下沉式調整
下面是代碼實現:
void AdjustDown(int array[],size_t size,size_t index)
{
size_t parent = index;
size_t child = 2 * index + 1 ;
while(child < size)
{
if(child + 1 < size && array[child] < array[child+1])
{
child = child + 1;
}
if(array[parent] < array[child])
{
Swap(&array[parent],&array[child]);
}
parent = child;
child = 2 * parent + 1;
}
return;
}
void HeapCreate(int array[],size_t size)
{
if(size <= 1)
{
return;
}
size_t i = (size -1 -1) / 2;
for(;i > 0;--i)
{
AdjustDown(array,size,i);
}
AdjustDown(array,size,0);
return;
}
void HeapPop(int array[],size_t heap_size)
{
if(heap_size <= 1)
{
return;
}
Swap(&array[0],&array[heap_size-1]);
AdjustDown(array,heap_size - 1,0);
}
void HeapSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//1.基於數組建立一個堆(如果是升序,就建立大堆)
HeapCreate(array,size);
//2.循環的刪除堆頂元素,將所有的元素都刪除完畢,排序完成
size_t i = 0;
for(;i < size;++i)
{
//第二個參數表示數組中哪部分區間是符合堆的規則
//第一個刪除之前,[0,size)都是堆
//第二次刪除之前,[0,size-1)都是堆
//第三次刪除之前,[0,size-2)都是堆
HeapPop(array,size-i);
}
return;
}