詳解選擇排序和對其優化
阿新 • • 發佈:2019-01-01
選擇排序是每次從未排序的佇列中取出最小值,然後插到已排序佇列的尾部。
例子: 12, 78, 9,7,25;待排序的陣列元素
[7] [78, 9, 12, 25] 進行第一次排序時,min = 7,將它插到已排序佇列的尾部,因為是第一次排序所以7 為已排序佇列的首部。這裡所說的插到已排序佇列的尾部,其實是將找到的最小值和已排序的後一位值做交換。
[7, 9] [78,12,25]
[7,9, 12] [78, 25]
[7,9,12, 25] [78]
[7,9,12, 25 78] 排序完成
void SelectSort(int number[]) { int i, j, min; for(i = 1; i < MAX; i++) { min = i; for(j = i+1; j <= MAX; j++) { if(number[min] > number[j]) min = j; } if(min != i) SWAP(number[i],number[min]); } printf("sorted : "); for(i = 1; i <= MAX; i++) printf("%d ",number[i]); printf("\n"); }
程式碼中每次用一個min 值來記錄最小值的下標,為了後面的方便使用,陣列從下標 1 開始計數
那麼選擇排序的演算法複雜度就是 n+(n-1)+.......+1 = (1+n)*n/2 = O(n^2)
從上述選擇排序的思想可以看出,這個n^2 主要消耗在尋找最小值上面,那麼欲對其進行優化,必須以極快的速度找到未排序數列中的最小值,在這裡我使用的是最小堆樹的結構,因為堆樹搜尋的路徑是樹根到葉子節點,而不是遍歷整個未排序的部分。
最小堆樹有2 個葉子節點,根的值是最小值,而2個葉子節點之間無須進行排序
下面是一個最小堆樹:
根據二叉樹的概念可以得出 root = leaf / 2;
假定10 個數字在陣列中,且下標是從 零開始
經過交換節點:得到一個最小堆樹
將根和最後一個葉子節點做交換, 取出最小值,並且繼續調整樹,使其滿足最小堆的性質
以此迴圈下去,直到最後一個數
#include <stdio.h> #include <time.h> #define MAX 10 #define SWAP(x,y){int t; t = x; x = y; y = t;} void UltimateSelectSort(int number[]) { int i,root,leaf,m; int heap[MAX+1]; for(i = 1; i <= MAX; i++) { heap[i] = number[i]; leaf = i; root = leaf/2; while(leaf >= 2 && heap[root]>heap[leaf]) { SWAP(heap[root], heap[leaf]); leaf = root; root = leaf/2; } } for(i = 1; i <= MAX; i++) number[i] = heap[i]; m = MAX; while(m > 1) { SWAP(number[1],number[m]); m--; root = 1; leaf = root * 2; while(leaf <= m) { if((leaf < m) && (number[leaf]>number[leaf+1])) leaf++; if(number[root] <= number[leaf]) break; SWAP(number[root], number[leaf]); root = leaf; leaf = root * 2; } } printf("sorted : "); for(i = MAX; i > 0; i--) printf("%d ", number[i]); printf("\n"); } int main() { int number[MAX+1]; int i; srand(time(NULL)); printf("Previous of sort : "); for(i = 1; i <= MAX; i++) { number[i] = rand() % 100; printf("%d ",number[i]); } printf("\n"); UltimateSelectSort(number); return 0; }
經過優化之後的演算法複雜度為O(n*log2n) 以2為底,可以看出,優化之後的效率明顯上升了一個檔次