標籤:style blog color io 使用 ar strong for 資料
排序是將一組對象按照一定的規則重新排列的過程。即使目前完全可以使用標準庫中的排序函數,學習排序演算法仍然有著較大意義:
排序演算法的學習可以協助你全面瞭解比較演算法效能的方法;
類似的技術上能有效解決其他類型的問題;
排序演算法通常是我們解決問題的第一步;
更重要的是這些演算法都很經典,優雅和高效。
排序在商業資料處理分析和現代科學中佔有重要的地位,其中快速排序演算法被譽為20世紀科學和工程領域十大演算法之一。今天我們要看的就是相對簡單但很經典的初級排序演算法,包括選擇排序,插入排序及Shell排序。
準備
開始之前,我們先約定好演算法類模版形式,其中我們將排序代碼放入sort()方法中,less()方法對元素進行比較返回bool值,exch()方法用於元素位置交換,形式如下:
public class Example { public static void sort(IComparable[] a) { //排序代碼 } private static void exch(IComparable[] a, int i, int j) { IComparable temp = a[i]; a[i] = a[j]; a[j] = temp; } private static bool less(IComparable v, IComparable w) { return v.CompareTo(w)<0; } private static void show(IComparable[] a) { //列印資料 } private static bool isSorted(IComparable[] a) { for(int i=1;i<a.Length;i++) { if(less(a[i], a[i-1])) return false; } return true; } }
這裡我使用C#實現,less()方法使用IComparable介面方法,適用於任何任何實現IComparable介面的資料類型進行排序,系統的Int, String等類型均實現了此介面。
選擇排序
概述:
首先找到數組最小的元素,將它和數組第一個元素交換位置。再次,在剩下的元素中找出最小的元素,將它與第二個元素位置交換。如此往複,直到將整個數組排序。因為它不斷地選擇剩餘元素之中的最小者,所以稱為選擇排序。
分析:
對於長度為N的數組,選擇排序需要大約(N^2)/2次比較和N次交換。其增長數量級為平方層級,是一種很容易理解和實現的簡單排序演算法。它有兩個鮮明的特點:
1.已耗用時間和輸入無關。通過實驗發現輸入一個有序數組和輸入一個隨機數組所有排序時間竟然一樣長。
2.資料移動是最少的。選擇排序只有N次交換,這是其他排序演算法無法做到的。
代碼實現:
public class Selection { public static void sort(IComparable[] a) { //排序代碼 for (int i = 0; i < a.Length; i++) { int min = i; for (int j = i + 1; j < a.Length; j++) { if (less(a[j], a[min])) min = j; } exch(a, min, i); } } private static void exch(IComparable[] a, int i, int j) { IComparable temp = a[i]; a[i] = a[j]; a[j] = temp; } private static bool less(IComparable v, IComparable w) { return v.CompareTo(w) < 0; } private static void show(IComparable[] a) { //列印資料 } private static bool isSorted(IComparable[] a) { for (int i = 1; i < a.Length; i++) { if (less(a[i], a[i - 1])) return false; } return true; } }
這裡遵循我們約定的規則設計了Selection演算法類,運行後,發現可以獲得正確的排序結果。
分別執行大小為100,1000,10000,100000的數組後發現,選擇排序在大規模排序時使用耗費的時間相當長,中小規模排序可以滿足基本使用。
插入排序
概述:
如同日常整理紙牌一樣,將每個元素插入到已有序的序列中適當位置,為了給插入的元素騰出空間,我們需要將其餘所有元素在插入之前都右移一位。這種演算法,我們稱為插入排序。
分析:
和選擇排序不同,插入排序的所需時間完全取決於輸入中元素的初始位置。例如,對一個很大的有序數組進行排序將會比對隨機順序的數組或者逆序數組進行排序要快得多。
對於隨機排列的長度為N且主鍵不重複的數組,平均情況下插入排序需要~(N^2)/4次比較及~(N^2)/4次交換。最壞情況下需要~(N^2)/2次比較及~(N^2)/2次交換,最好情況下需要N-1次比較和0次交換。
實現:
public class Insertion { public static void sort(IComparable[] a) { //排序代碼 int N = a.Length; for (int i = 1; i < N; i++) { for (int j = i; j > 0 && less(a[j], a[j - 1]); j--) exch(a, j, j - 1); } } //最佳化交換次數 public static void fasterSort(IComparable[] a) { int N = a.Length; for (int i = 1; i < N; i++) { IComparable temp = a[i]; int j = i - 1; while (j > 0 && less(temp, a[j])) { a[j + 1] = a[j]; j--; } a[j + 1] = temp; } } private static void exch(IComparable[] a, int i, int j) { IComparable temp = a[i]; a[i] = a[j]; a[j] = temp; } private static bool less(IComparable v, IComparable w) { return v.CompareTo(w) < 0; } private static void show(IComparable[] a) { //列印資料 for (int i = 1; i < a.Length; i++) { Console.WriteLine(a[i]); } } private static bool isSorted(IComparable[] a) { for (int i = 1; i < a.Length; i++) { if (less(a[i], a[i - 1])) return false; } return true; }}
通過多次實驗輸入,發現插入排序在處理大規模資料時也相當慢。但是和選擇排序不同,當輸入為較大大規模有序資料(如大小100000時),速度要快的多,而選擇排序則依舊需要漫長的排序時間,這點是優於選擇排序的。
演算法手記(5)初級排序演算法