排序是將一組物件按照一定的規則重新排列的過程。即使目前完全可以使用標準庫中的排序函式,學習排序演算法仍然有著較大意義:
排序演算法的學習可以幫助你全面了解比較演算法效能的方法;
類似的技術上能有效解決其他型別的問題;
排序演算法通常是我們解決問題的第一步;
更重要的是這些演算法都很經典,優雅和高效。
排序在商業資料處理分析和現代科學中占有重要的地位,其中快速排序演算法被譽為20世紀科學和工程領域十大演算法之一。今天我們要看的就是相對簡單但很經典的初級排序演算法,包括選擇排序,插入排序及shell排序。
準備
開始之前,我們先約定好演算法類模版形式,其中我們將排序**放入sort()方法中,less()方法對元素進行比較返回bool值,exch()方法用於元素位置交換,形式如下:
publicclass
example
private
static
void exch(icomparable a, int i, int
j)
private
static
bool
less(icomparable v, icomparable w)
private
static
void
show(icomparable a)
private
static
bool
issorted(icomparable a)
return
true
; }
}
這裡我使用c#實現,less()方法使用icomparable介面方法,適用於任何任何實現icomparable介面的資料型別進行排序,系統的int, string等型別均實現了此介面。
選擇排序
概述:首先找到陣列最小的元素,將它和陣列第乙個元素交換位置。再次,在剩下的元素中找出最小的元素,將它與第二個元素位置交換。如此往復,直到將整個陣列排序。因為它不斷地選擇剩餘元素之中的最小者,所以稱為選擇排序。
分析:對於長度為n的陣列,選擇排序需要大約(n^2)/2次比較和n次交換。其增長數量級為平方級別,是一種很容易理解和實現的簡單排序演算法。它有兩個鮮明的特點:
1.執行時間和輸入無關。通過實驗發現輸入乙個有序陣列和輸入乙個隨機數組所有排序時間竟然一樣長。
2.資料移動是最少的。選擇排序只有n次交換,這是其他排序演算法無法做到的。
**實現:
publicclass
selection
exch(a, min, i);}}
private
static
void exch(icomparable a, int i, int
j)
private
static
bool
less(icomparable v, icomparable w)
private
static
void
show(icomparable a)
private
static
bool
issorted(icomparable a)
return
true
; }
}
這裡遵循我們約定的規則設計了selection演算法類,執行後,發現可以獲得正確的排序結果。
分別執行大小為100,1000,10000,100000的陣列後發現,選擇排序在大規模排序時使用耗費的時間相當長,中小規模排序可以滿足基本使用。
插入排序
概述:如同日常整理紙牌一樣,將每個元素插入到已有序的序列中適當位置,為了給插入的元素騰出空間,我們需要將其餘所有元素在插入之前都右移一位。這種演算法,我們稱為插入排序。
分析:和選擇排序不同,插入排序的所需時間完全取決於輸入中元素的初始位置。例如,對乙個很大的有序陣列進行排序將會比對隨機順序的陣列或者逆序陣列進行排序要快得多。
對於隨機排列的長度為n且主鍵不重複的陣列,平均情況下插入排序需要~(n^2)/4次比較及~(n^2)/4次交換。最壞情況下需要~(n^2)/2次比較及~(n^2)/2次交換,最好情況下需要n-1次比較和0次交換。
實現:
publicclass
insertion
}//優化交換次數
public
static
void
fastersort(icomparable a)
a[j + 1] =temp; }}
private
static
void exch(icomparable a, int i, int
j)
private
static
bool
less(icomparable v, icomparable w)
private
static
void
show(icomparable a)
}private
static
bool
issorted(icomparable a)
return
true
; }
}
通過多次實驗輸入,發現插入排序在處理大規模資料時也相當慢。但是和選擇排序不同,當輸入為較大規模有序資料(如大小100000時),速度要快的多,而選擇排序則依舊需要漫長的排序時間,這點是優於選擇排序的。
shell排序
概念:希爾排序是基於插入排序演算法的,但是更為快速。對於大規模亂序陣列插入排序很慢,它們只會交換相鄰的元素,因此元素只能一點一點地從一端移動到另一端。希爾排序為了加快速度,簡單地改進了插入排序,交換不相鄰的元素以對陣列的區域性進行排序,並最終使用插入排序對區域性有序的陣列排序。
分析:希爾排序更高效的原因在於它權衡了子陣列的規模的和有序性排序之初,各個子陣列都很短,排序之後子陣列都是部分有序的,這兩種情況很適合插入排序。子陣列部分有序的程度取決於遞增序列的選擇,透徹理解希爾排序的效能至今仍是乙個挑戰,實際上它是我們唯一無法準確描述其對於亂序陣列效能特徵的排序方法。
實現:
publicclass
shell
a[j + h] =temp;
//while寫法
//j = i - h;
//while (j > 0 && less(temp, a[j]))
//
//a[j + h] = temp;
} h /= 3
; }
}private
static
void exch(icomparable a, int i, int
j)
private
static
bool
less(icomparable v, icomparable w)
private
static
void
show(icomparable a)
}private
static
bool
issorted(icomparable a)
return
true
; }
}
我多次實驗後,對shell排序的效率驚嘆不已。大小為100萬的陣列排序完成只需要4s,而插入排序和選擇排序5分鐘都沒有排序完,這實在是太神奇了,學到這裡,我是真心的感慨數學的威力,簡單的改進就能讓短短的**爆發如此強大的力量。
通過初級排序演算法的學習和動手實現後,我開始清晰明白乙個理念:通過提公升速度來解決其他方式無法解決的問題是研究演算法的主要原因。
演算法手記(5)初級排序演算法
排序是將一組物件按照一定的規則重新排列的過程。即使目前完全可以使用標準庫中的排序函式,學習排序演算法仍然有著較大意義 排序演算法的學習可以幫助你全面了解比較演算法效能的方法 類似的技術上能有效解決其他型別的問題 排序演算法通常是我們解決問題的第一步 更重要的是這些演算法都很經典,優雅和高效。排序在商...
初級排序演算法
回顧之前學習的各種排序演算法,從初級到高階,包括選擇排序,氣泡排序,插入排序,希爾排序,快速排序,歸併排序,堆排序等等,持續更新中 注 這裡實現的演算法都是遞增排序,也就是從小到大排序。思想 首先,找到陣列中最小的那個元素,其次,將它和陣列的第乙個元素交換位置 如果第乙個元素就是最小元素那麼它就和自...
初級排序演算法
排序模板 public class example private static boolean less comparable v,comparable w private static void exch comparable a,int i,int j private static void ...