幾種trivial排序方法的分析

一提到排序可能很多人就立即想到大名鼎鼎的快速排序,不過今天我想對幾種O(n2)的排序方法做一個簡單的分析,他們是選擇排序(selection sort),插入排序(insertion sort)和冒泡排序(bubble sort),經過分析我們會知道在某些場合下,他們是有價值的,速度上也可以秒殺快排。

 

選擇排序:首先找到數組中最小的元素,將其與位置上第一的元素交換位置;然後找到第二小的元素,將其與位置上第二的元素交換位置;持續這樣的操作直到數組完全被排序。

 

插入排序:從前向後掃描數組中的每個元素,通過不斷將當前元素插入到該元素之前的序列中來保持掃描過的序列是有序的,這樣處理完最後一個元素後,整個數組就是有序的了。

 

冒泡排序:不斷從最後一個位置向前掃描數組,每次掃描的長度減1,在掃描的過程中遇到相鄰的元素不滿足排序規則就將它們交換,直到掃描長度降爲1。

 

下面是這幾種排序的c和c++的源碼:

選擇排序c版本:

插入排序c版本:

冒泡排序c版本:

 

選擇排序c++版本:

插入排序c++版本:

冒泡排序c++版本:

 

我們分析複雜度一般使用比較次數作爲度量,可是在某些條件下,元素移動實際上是時間的瓶頸,所以下面分別從兩個方面分析這幾種排序的複雜度,在這裏我們令數組的長度爲n。

 

首先看選擇排序,在掃描的過程中,每個內層循環都要完整的掃描剩餘的數組,這樣比較的次數爲n+(n-1)+……+1=n(n-1)/2,大概是n2/2。這說明選擇排序的比較次數並不依賴於數組的排列,也就是說一個趨近於有序的數組使用選擇排序並沒有得到額外的好處。但是選擇排序的優點在於它固定的使用了n-1次交換操作,所以在元素移動的複雜度上,選擇排序是線性的。

 

再來看插入排序,插入排序平均看來需要n2/4次比較,我們可以這樣分析,每次插入操作時按平均意義的話元素應該向前移動一半的距離,而這樣每個元素都移動一半的距離就相當於選擇排序一半的比較次數。同時我們還可以看出插入排序的比較次數和元素移動次數幾乎是一樣的,所以元素交換的平均次數也是n2/4。這樣看來插入排序好像並沒有什麼優勢可言,相比選擇排序還有一方面可以到達線性複雜度來說,插入排序兩方面都是二次的。不過大家注意剛纔的分析是平均意義上的,考慮數組如果已經有序的話,實際上插入排序無論從比較還是元素移動都是線性的。換句話說,插入排序可以利用到數組未排序前的結構。關於這個問題的精確分析,參考後面的附言。

 

最後是冒泡排序, 冒泡排序相比其他兩個排序幾乎沒有優勢,即使從平均意義上講,它的比較複雜度和元素移動複雜度相比最壞情況也沒有任何提升,都是大概n2/2。最壞情況很容易分析,第k次冒泡要比較並交換n-k次,所以總的來說是n(n-1)/2。而平均意義感覺可能會好一些,但是並不是那樣的,分析的過程比較複雜,這裏就不列出了。所以一般來說,冒泡排序都要比選擇排序和插入排序要慢。

 

結論:1.在移動元素佔主要時間的應用中選擇排序是非常好的選擇 。

         2.對於幾乎有序的數組進行排序應該使用插入排序。

以上的情況從複雜度上是可以擊敗快速排序的,這裏沒有給出具體的實驗結果。

 

附言:

我們來精確的分析一下插入排序的比較次數,首先介紹兩個概念“逆序”和“逆序數”,如果數組中某兩個元素的位置上的次序和他們的大小上的次序是相反的,那麼這就叫做一個逆序,數組中所有的逆序的總數叫做這個數組的逆序數。對於數組中某一個元素來說,它與位置上在它前面的所有元素的逆序的個數就是前面比他大的那些元素的個數,而考慮插入排序的內循環,當前元素被比較的次數最多和前面比他大的那些元素的個數大1。這就說明插入排序的內循環的複雜度與對於某個元素與其前面元素的逆序個數是相同的,我們將所有的元素都考慮進來就能得出結論:數組的逆序數與插入排序的比較次數是幾乎相同的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章