插入排序是一種(或者一類)簡單的排序算法。之因此說它是一類算法,是由於插入排序能夠有變種,例如二分查找插入排序。
java
本文先討論直接插入排序(簡單插入排序)。算法
算法概述/思路編程
直接插入排序的思路很容易理解,它是這樣的:數組
1.把待排序的數組分紅已排序和未排序兩部分,初始的時候把第一個元素認爲是已排好序的。性能優化
2.從第二個元素開始,在已排好序的子數組中尋找到該元素合適的位置並插入該位置。數據結構
3.重複上述過程直到最後一個元素被插入有序子數組中。編程語言
4.排序完成。ide
下面是插入排序的示意圖(圖片來自維基百科):oop
思路很簡單,但代碼並不是像冒泡排序那麼好寫。首先,如何判斷合適的位置?大於等於左邊、小於等於右邊?不行,不少邊界條件須要考慮,並且判斷次數太多。其次,數組中插入元素,必然須要移動大量元素,如何控制它們的移動?性能
實際上,這並非算法自己的問題,而多少和編程語言有點關係了。有時候算法自己已經很成熟了,到了具體的編程語言仍是要稍做改動。這裏講的是Java算法,那就拿Java說事兒。
爲了解決上述問題,咱們對第二步稍做細化,咱們不從子數組的起始位置開始比較,而從子數組尾部開始逆序比較,只要比須要插入的數大,就向後移動。直到不大於該數的數,那麼,這個空出來的位置就安放須要插入的數字。所以,咱們能夠寫出如下代碼:
public static void insertionSort(int[] arr){ for (int i=1; i<arr.length; ++i){ int value = arr[i]; int position; for (position=i; position>0 ; --position){ if (arr[position-1] > value){ arr[position]=arr[position-1]; } else{ break; } }// loop position arr[position] = value; }//loop i }
這段代碼寫得很不優雅,看着不舒服。稍做修改,能夠獲得下面的代碼(我以爲這個要好看些,也許有點小強迫症):
代碼實現
public static void insertionSort(int[] arr){ for (int i=1; i<arr.length; ++i){ int value = arr[i]; int position=i; while (position>0 && arr[position-1]>value){ arr[position] = arr[position-1]; position--; } arr[position] = value; }//loop i }
算法性能/複雜度
如今討論下直接插入算法的時間複雜度。不管輸入如何,算法總會進行n-1輪排序。可是,因爲每一個元素的插入點是不肯定的,受輸入數據影響很大,其複雜度並非必定的。咱們能夠分最佳、最壞、平均三種狀況討論。
1.最佳狀況:由算法特色可知,當待排數組自己即爲正序(數組有序且順序與須要的順序相同,於咱們的討論前提,即爲升序)時爲最佳,理由是這種狀況下,每一個元素只須要比較一次且無需移動。算法的時間複雜度爲O(n);
2.最壞狀況:很顯然,當待排數組爲逆序時爲最壞狀況,這種狀況下咱們的每輪比較次數爲i-1, 賦值次數爲i。總的次數爲級數2n-1的前n項和,即n^2.算法的時間複雜度爲O(n^2);
3.平均狀況:由上述分析能夠獲得平均狀況下算法的運算次數大約爲(n^2)/2(注:這裏計算以賦值和比較計,若按移動和比較,則大約爲n^2/4),顯然,時間複雜度仍是O(n^2)。
至於算法的空間複雜度,全部移動均在數據內部進行,惟一的開銷是咱們引入了一個臨時變量(有的數據結構書上稱爲「哨兵」),所以,其空間複雜度(額外空間)爲O(1)。
算法穩定性
因爲只須要找到不大於當前數的位置而並不須要交換,所以,直接插入排序是穩定的排序方法。
算法變種
若是待排列的數據比較多,那麼每次從後往前找就形成了很大的開銷,爲了提升查找速度,能夠採用二分查找(Binary Search)進行性能優化。因爲二分查找的效率很高,保證了O(㏒n)複雜度,在數據比較多或輸入數據趨向最壞狀況時能夠大幅提升查找效率。在有些書上將這種方法稱爲折半插入排序。它的代碼實現比較複雜,之後有時間能夠貼出來。
此外,還有2-路插入排序和表插入排序。2-路插入排序是在折半插入排序的基礎上進一步改進,其移動次數大爲下降,大約爲n^2/8。可是,它並不能避免移動次數,也不能下降複雜度級別。表插入排序則徹底改變存儲結構,不移動記錄,但須要維護一個鏈表,以鏈表的指針修改代替移動記錄。所以,其複雜度仍然是O(n^2)。
有關2-路插入排序和表插入排序,能夠參考嚴蔚敏、吳偉民編著的《數據結構》一書。
算法適用場景
插入排序因爲O(n^2)的複雜度,在數組較大的時候不適用。可是,在數據比較少的時候,是一個不錯的選擇,通常作爲快速排序的擴充。例如,在STL的sort算法和stdlib的qsort算法中,都將插入排序做爲快速排序的補充,用於少許元素的排序。又如,在JDK 7 java.util.Arrays所用的sort方法的實現中,當待排數組長度小於47時,會使用插入排序。
參考資料
1.維基百科 http://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F
2.《數據結構》(C語言版)第10章第2節,嚴蔚敏、吳偉民著,清華大學出版社,2007年。