經典排序演算法系列之二 選擇排序

2021-06-07 12:11:11 字數 3399 閱讀 2386

2.1基本思想

第i趟排序開始時,當前有序區和無序區分別為r[0..i-1]和r[i..n-1],該趟排序則是從當前無序區中選出關鍵字最小的記錄r[k],將它與無序區的第乙個記錄r[i]交換,使得r[0..i] 和r[i+1..n-1]分別變為新的有序區和無序區,經過i-1趟排序後,整個表遞增有序。

2.2演算法思路

首先我們要做的就是從無序區中選出最小值下標,看看下面的**:

int i;

int temp;

//儲存最小值下標

int key = 0;

//遍歷陣列查詢最小值下標

for(i = 0; i < length; i++)

進行排序,排序過程如下:

第一趟:無序區最小值下標為3,則a[0]和a[3]交換位置,陣列為a=,有序區為a[0],無序區為a[1..6];

第二趟:無序區最小值下標為5,則a[1]和a[5]交換位置,陣列為a=,有序區為a[0..1],無序區為a[2..6];

第三趟:無序區最小值下標為2,則a[2]不需要移動位置,陣列為a=,有序區為a[0..2],無序區為a[3..6];

第四趟:無序區最小值下標為5,則a[3]和a[5]交換位置,陣列為a=,有序區為a[0..3],無序區為a[4..6];

第五趟:無序區最小值下標為4,則a[4]不需要移動位置,陣列為a=,有序區為a[0..4],無序區為a[5..6];

第六趟:無序區最小值下標為6,則a[5]和a[6]交換位置,陣列為a=,有序區為a[0..5],無序區為a[6];

上面六步完成了對陣列a的排序,現在我們可以按照上面的步驟寫出直接選擇排序演算法的核心**:

//直接選擇排序演算法

void straightselectsort(int *a, int length)

} //如果無序區最小值不在無序區頭部則交換位置到無序區頭部

if(key != i)}}

2.3**效率在直接選擇排序中,共需要進行n-1次選擇和交換,每次選擇需要進行n-i 次比較 (1<=i<=n-1),而每次交換最多需要3次移動,因此,總的比較次數c=1/2(n*n - n),,總的移動次數3(n-1)。由此可知,直接選擇排序的時間複雜度為o(n^2) (n的平方),所以當記錄占用位元組數較多時,通常比直接插入排序的執行速度快些。由於在直接選擇排序中存在著不相鄰元素之間的互換,因此,直接選擇排序是一種不穩定的排序方法。

3.1基本思想

想要了解堆排序的基本思想,我們首先要了解大根堆和小根堆的含義。大根堆就是任何父親結點值大於左右孩子結點值的完全二叉樹,而小根堆就是任何父親結點值小於左右孩子結點值的完全二叉樹。堆排序就是先將初始檔案r[1..n]建成乙個大根堆,此堆為初始的無序區,再將關鍵字最大的記錄r[0](即堆頂)和無序區的最後乙個記錄r[n]交換,由此得到新的無序區r[1..n-1]和有序區r[n],且滿足r[1..n-1]<=r[n],由於交換後新的根r[1]可能違反堆性質,故應將當前無序區r[1..n-1]調整為堆。然後再次將r[1..n-1]中關鍵字最大的記錄r[1]和該區間的最後乙個記錄r[n-1]交換,由此得到新的無序區r[1..n-2]和有序區r[n-1..n],且仍滿足關係r[1..n-2]<=r[n-1..n],同樣要將r[1..n-2]調整為堆。

3.2演算法思路

首先解釋一下完全二叉樹的概念:若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的節點都連續集中在最左邊,這就是完全二叉樹。下面我給大家展示大根堆和小根堆,如下圖所示:

根據上面對大根堆和小根堆概念的闡述,上面兩個堆符合要求,我們從中可以看出,在大根堆中根結點值最大(小根堆中根結點值最小)。在堆排序演算法中正式利用大根堆(或者小根堆)這乙個特徵來進行排序。所以在排序之前,我們必須使用輸入陣列建立好大根堆(或者小根堆)。

當我們得到輸入陣列的時候,我們可以把它看成乙個無序的堆。假設陣列a的長度為n,陣列元素從頭到尾分別對應1,2,3,...,n,則由二叉樹的特點,我們知道每乙個結點i的左右孩子結點分別對應2*i和2*i+1,這樣我們就可以建立相應的堆的模型,建立了堆的模型之後,我們還要將這個堆調整為大根堆(或者小根堆),為了讓大家對這個過程有乙個更直觀的印象,我以陣列a=為例來建立堆模型並調整為大根堆,如下圖所示:

由上圖步驟可知,該方法是將孩子結點中的最大值往上浮,而下沉的結點可能會破壞子樹的大根堆性質,所以繼續對子樹進行調整,最終得到乙個最大堆。根據圖示的步驟我們可以很輕鬆的實現其核心**:

//實現最大堆

void maxheapify(int *a, int i)

else

//父親結點不是較大值則與較大孩子結點交換

if(max != i)

//當孩子結點與父親結點交換位置後,子樹可能不是最大堆,對交換的孩子結點呼叫本函式,讓子樹成為最大堆

maxheapify(a,max);

}

我們已經為建立大根堆完成核心工作,剩下的工作就不難完成了,要建立大根堆,我們就必須對完全二叉樹從下至上的每乙個非葉子結點呼叫一下maxheapify即可,實現建立大根堆的核心**如下:

//建立大根堆

void buildmaxheap(int *a, int length)

}

在建立大根堆之後,整個樹的最大結點一定是根結點,我們要做的就是將大根堆的根結點與a[n]交換位置,於是我們再以陣列a[0..n-1]建立大根堆,將根結點與a[n-1]交換位置,以此類推,當大根堆只剩下乙個元素時,整個陣列已經排好序。實現排序的核心**如下:

//堆排序演算法

void heapsort(int *a, int length)

}

3.3**效率堆[排序的時間,主要由建立初始]堆和反覆重建堆這兩部分的時間開銷構成,它們均是通過呼叫heapify實現的。堆排序的最壞時間複雜度為o(nlogn)。堆序的平均效能較接近於最壞效能。由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的檔案。堆排序是就地排序,輔助空間為o(1),它是不穩定的排序方法。

《演算法導論》機械工業出版社

thomas h.cormen   charles e.leiserson   ronlad l.rivest   clifford stein

經典演算法系列之 選擇排序

1 前言 演算法,在計算機中的地位,就相當於人類大腦的決策中樞系統,哪怕最簡單的演算法,其精妙的思維方式,都可以讓人開啟一扇新的視窗。演算法,它不僅僅只是狹義的用來解決電腦科學領域的問題,更是一種 思維方式 演算法思維,是一種深度思考和創造的過程。演算法,只有真正理解了,而不只是所謂的知道,並將應用...

經典排序演算法之二 選擇排序

思想 假設給定乙個大小為n的陣列,從中選出最大的值 記錄下標 與下標n i的值進行交換 i 1,n 1 i為迴圈次數 即遍歷一次交換一次,每次遍歷確定乙個最大值。優化思路 使用一種快速查詢最值的方法可降低選擇排序的時間複雜度,例如使用堆這種資料結構,可在o logn 的情況下找到最值 普通 cmp ...

排序演算法系列 選擇排序

選擇排序可以說是眾多排序演算法中,最基礎 最直觀的乙個演算法了。它的思想十分簡單 遍歷列表,找出最小的乙個數,記下索引 將最小的數新增到新的列表中,同時刪除原陣列中的數 重複第一步 舉個例子 假如現在有乙個無序陣列disorder arr 4,2,19,10,1 和乙個空陣列order arr 第一...