文章來源:http://blog.seclibs.com/算法之排序(上)/算法
排序算法有不少種,甚至有不少都徹底沒有聽過,咱們最多見,也最經典的就是:冒泡排序、插入排序、選擇排序、歸併排序、快速排序、計數排序、基數排序、桶排序。數組
按照時間複雜度來進行劃分能夠將其劃分爲三類數據結構
此次咱們來講時間複雜度爲O(n2)的性能
在說具體的排序方法以前,先明確排序算法的評價標準blog
首先是排序算法的執行效率,執行效率通常從最好、最壞、平均時間複雜度上分析,其分析時間複雜度時須要考慮係數、常數和低階,由於時間複雜度是在數據規模特別大的時候的增加趨勢,在平時的代碼中,數量級都是比較小的,因此還須要考慮這些問題。在基於比較的排序算法中,數值比較的次數和數據的移動次數也都是須要考慮進去的。排序
其次是內存的消耗,算法的內存消耗能夠用空間複雜度來表示,當空間複雜度爲O(1)的算法也能夠稱之爲原地排序算法。內存
最後是算法的穩定性,當一組數據中有兩個相同的值時,排序以後兩個值的順序是若是沒有交換那它就是具備穩定性的算法。文檔
而後咱們再引入兩個概念,有序度和逆序度get
有序度是數組中具備有序關係的元素對的個數。博客
好比說二、四、三、一、五、6這組數組的有序度是11,由於它有11個有序元素對,分別是(2,4)、(2,3)、(2,5) (2,6)、(4,5)、(4,6)、(3,5)、(3,6)、(1,5)、(1,6)、(5,6)。
對於六、五、四、三、二、1這組數據,它的有序度是0;對於一、二、三、四、五、6這組數據來講,它的有序度是n*(n-1)/2,這種徹底有序的數組的有序度叫作滿有序度。
因此逆序度也就等於滿有序度-有序度,排序的過程就是增長有序度,減小逆序度的過程,最後到達滿有序度,排序就結束了。
冒泡排序是依次對兩個相鄰的值進行比較,若是知足要求的大小關係,就繼續日後看,若是不知足就將它們兩個互換位置。 每一次冒泡都最起碼會交換一個值 ,因此最多執行n次就能夠完成排序操做。
若是一組數據爲四、五、六、三、二、1,要求其從小到大排序,那它進行第一次冒泡排序的過程就是這樣的
這個時候,最大的數字6,就已經排在了正確的位置上,後續的冒泡操做依次是這樣的
這種狀況是恰好用了n次,若是代碼直接寫成循環n次的話,就可能會有多餘的判斷流程,若是排列順序恰好爲一、二、三、四、六、5的話,就只須要一次就能夠完成排序操做,因此咱們能夠將每次有無數據交換的操做來做爲有無完成排序的條件。
首先冒泡排序只交換相鄰兩個數據,因此空間複雜度爲O(1),是原地排序算法。
在冒泡排序中,咱們能夠規定它在兩個數值相等的時候不進行交換,就能夠保證排序先後相同的數值前後順序不變,因此它是一個穩定的排序算法。
在數組中,咱們在中間插入一個數據的時候,是把相應位置的數據都日後挪一位,同時還能夠保證順序不變,一樣的方法放到算法中,就有了插入排序。
首先是將數據分爲兩個區間,已排序區間和未排序區間,開始的時候,已排序區間只有一個元素,而後在未排序區間中取一個元素,在已排序區間中尋找對應的位置將其插入,不斷重複直到未排序區間的數據爲空。
好比要將四、五、六、一、三、2這組數據進行排序,那過程就是這樣的
插入排序與冒泡排序同樣,有比較和移動兩個操做,循環比較獲得要插入的位置,而後將後面的元素日後挪一位。
對於不一樣的查找插入點方法,不論是從頭至尾,仍是從尾到頭,元素的比較次數是有區別的。但對於一個給定的初始序列,移動操做的次數老是固定的,就等於逆序度。
仍是拿剛剛的例子說,滿有序度是n*(n-1)/2=15,初始序列的有序度是5,因此逆序度是10。
在插入排序的時候,咱們並不須要額外的空間,因此空間複雜度是O(1),因此它是一個原地排序算法。
由於在插入的時候,咱們可讓未排序區間的元素排在已排序區間中相同元素的後面,因此它也是一個穩定的排序算法。
選擇排序和插入排序比較相似,也有已排序區間和未排序區間,可是初始的時候是不對其進行區分的,選擇排序每次都會在未排序區間中選擇最小的一個數,將它放到已排序區間的末尾,若是沒有已排序區間就將其放到未排序區間的前面。
首先選擇排序也不須要申請多餘的內存空間,空間複雜度爲O(1),是原地排序算法。
因爲它將數值與前面的數值進行交換,這樣將會破壞它的穩定性,因此它是一個不穩定的排序算法。
相比之下,選擇排序比冒泡排序和插入排序稍微遜色一點,拿冒泡排序和插入排序相比呢,從代碼上看的話,冒泡排序進行數據交換是須要有第三個變量來作交換的,有三個賦值語句,而插入排序只須要一個賦值語句便可完成賦值操做,因此插入排序比冒泡排序性能更好一點。
參考文檔
極客時間-數據結構與算法之美
文章首發公衆號和我的博客
公衆號:無意的夢囈(wuxinmengyi)