我們將會詳細的介紹冒泡排序、插入排序、希爾排序以及選擇排序,下篇博客將繼續介紹堆排序、歸并排序以及快速排序的相關內容。當然上述內容的代碼實現我們依然采用Swift面向對象語言來實現。
本篇的思路與以往博客的思路一直,先分析每種排序的規則,然后給出原理示意圖,最后根據示意圖給出相應的代碼實現。編程這東西,只要是思路清晰,給出相應的代碼實現并不困難,本篇是使用Swift語言來實現的,如果你對Swift語言不熟悉,你可以選擇其他你熟悉的語言來實現。雖然語言不同,但是思路和方法都是一樣的。廢話少說,開始今天博客的主題。
一、排序協議的定義
在博客的開頭的,我們先給出排序協議的定義。因為我們本篇博客含有多種排序方式,為了使每種排序方法對外調用方式一致,我們需要定義一個排序的相關協議。所有排序的相關類都必須遵循該協議,讓此協議來定義具體的排序類對外的調用方式。
下方的SortType協議就是我們定義的排序類型的協議。其中的sort()方法是遵循該協議的類必須要實現的方法。sort()函數的參數是一個含有Int類型的數組,該數組就是要排序的數組。該方法的返回值是已經被排好序的數組。具體代碼如下所示。
** 二、冒泡排序**
接下來我們來聊一下冒泡排序,冒泡排序就像其名字一樣,還是比較生動的。在冒泡排序過程中會將數組分成兩部分,一部分是已經有序的數列,一部分是無序的數列。無序數列中不斷的將其中最小的值往有序序列中冒泡,泡冒完后,我們的序列就創建好了。本部分,我們將要給出冒泡排序的示意圖,已經相應的代碼實現。
1、冒泡排序示意圖
下方就是第一輪冒泡的具體過程,我們要對[62, 88, 58, 47, 62, 35, 73, 51, 99, 37, 93]序列進行冒泡排序。經過第一輪的冒泡后,該序列中最小的值35被冒到了數列的最前方。因為冒泡的過程是<typo id="typo-802" data-origin="挨個" ignoretag="true">挨個</typo>比較已經交換的過程。元素狀態我們的泡中是93,93與前一個值37進行比較,發現37要小于93,所以將泡中的值改成37,并往前移動。緊接著37在與前面的99比較,發現泡中的值要小,此刻不更新泡中的值并往前移動一個格。以此類推,無序序列中最小的值就會被冒到序列的起始位置。
每輪冒泡都會從無序序列中冒出那個最小的值,所以經過n(數列有n個值)次冒泡后,我們的數列就是有序的了。冒泡過程中的比較與交換的具體步驟如下所示:
2.代碼實現
根據上述示意圖,我們很容易給出下方的代碼。下方就是冒泡排序的代碼,BubbleSort這個類就是冒泡排序所對應的類,此類為了統一對外的調用方式,所以必須要遵循我們上一部分所定義的SortType協議。
說白了,冒泡的過程就是不斷比較和交換的過程,如果前一個值比后一個值要大,那么就要進行交換了
3、運行結果
下方代碼就是上述BubbleSort類所運行的具體結果。排序結果中詳細的打印了冒泡排序在每一輪冒泡中每一步要做的事情,具體如下所示。
三、插入排序
插入排序算是比較好理解的排序方式,插入排序也是將要排序的數列分為兩部分,前半部分是已經排好序的,后半部分則是無序的。插入排序中的插入是指“取出無序數列中第一個值,插入到有序數列中相應的位置”。其實這個插入過程也是不斷比較和交換的過程。
1、插入排序示意圖
下方就是插入排序的示意圖,紅色部分是有序數列,而綠色部分是無序數列。每一輪插入都會取出無序數列中的第一個元素插入到有序數列中,這個插入的過程其實就是一個比較交換的過程,如果要插入的值比前面的值要小,就要交換,直到不能交換為止。下方就是插入排序的過程。具體如下所示:
2、代碼實現
有了上述的示意圖,給出相應的代碼實現并不困難。代碼的核心思想就是通過循序不斷從無序數列中取出值,然后循環遍歷有序數列尋找合適的插入點。在下方中有兩個循環嵌套,外層循環負責不斷從無序序列中取值,然后通過內層循環將外層循環取出的值插入到有序數列中相應的位置,具體如下代碼所示:
3、運行結果
下方是運行結果的截圖,該運行結果其實就是插入排序的詳細過程。每一輪插入的過程就是有序序列增加,無序序列減少的過程。下方就是插入過程的詳細信息。
四、希爾排序
因為這個排序是一個叫希爾的人發明的,所以就叫希爾排序了。其實希爾排序是插入排序的升級版, 希爾排序根據其排序的特點又叫做縮小增量排序。希爾排序的大體步驟就是先將無序序列按照一定的步長(增量)分為幾組,分別將這幾組中的數據通過插入排序的方式將其進行排序。然后縮小步長(增量)分組,然后將組內的數據再次進行排序。知道增量為1位置。經過上述這些步驟,我們的序列就是有序的了。其實上述的插入排序就是增量為1的希爾排序,下方會給出相應的示意圖以及代碼實現。
1.希爾排序示意圖
下方就是希爾排序的詳細步驟,接下來我們將會對每一步進行詳細的解說。如下所示:
- (1)、首先按照增量進行分組,因為我們要排序的數列有11個,增量初始值是step = 11 / 2 = 5。也就是按照增量為5的步長對數組進行分組。在下方第一步中就是按照增量為5的方式進行分組的。我們將為一組的元素使用直線進行相連,分完組后,我們就將組內中的元素進行插入排序。
- (2)、將上一步使用的增量進行縮小,也就是本步驟的step = 5 / 2 = 2。 本部分,就要按照2的增量將上一步排序后的數組進行分組,然后再次將每個組內的數據進行插入排序。
- (3)、再次縮小增量,此刻step = 2 / 2 = 1, 當增量為1時,其實就是我們上一部分的插入排序。將整個數組進行插入排序,然后我們的數組就是有序的了。
具體示意圖如下所示:
2、希爾排序的代碼實現
根據上述的步驟,然后再結合著插入排序的代碼,給出希爾排序相應的代碼并不困難。就是將插入排序的步長從1修改成我們每次生成的步長即可,每次增量排序完畢后,我們要對增量按照相應的規則進行縮小即可。下方就是希爾排序的代碼實現。
在下方代碼中,最外層循環負責增量的生成和縮減,里邊的雙重循環就是我們之前我們插入排序的代碼,不步長要使用我們希爾排序生成的step,具體代碼如下所示:
3、運行結果
下方就是Shell排序的運行結果,從下方結果中我們不難看出增量是逐漸減小的。下方的輸出結果結合著本部分第一部分的示意圖看更為直觀一些。
** 五、選擇排序**
接下來來聊聊選擇排序,選擇排序也是比較好理解的。在選擇排序過程中,數組仍然被分作有序和無序兩部分。而選擇排序中的“選擇”是指不斷從無序序列中選擇最小的值放入到有序序列的最后的位置,換句話說就是從現有的無序序列中找出那個最小的值,然后與無序序列的第一個值進行交換,然后縮小無序序列的范圍即可。因為有序序列的最后一個值與無序序列的第一個值緊挨著,交換后,這個無序序列中的第一個值就成了有序序列的最后一個值。重復這個選擇的過程,我們的數組就會變得有序。下方將會給出詳細的示意圖以及相應的代碼實現。
**1.選擇排序示意圖 **
下方就是簡單選擇排序的部分步驟,只需要重復下方的步驟就可以通過選擇排序將我們的數組變成有序的序列。下方是對下方步驟的詳細介紹:
- 初識狀態下,我們整個數組就是無序的,從整個數組中我們找到了最小的元素35,其下標為5。然后將35與無序序列第一個元素62進行交換。交換后,有序序列中就有了一個值:35,而無序序列中就少了一個值:35。無序序列中的第一個值也就是變成了88。
- 再次從無序序列中選擇那個最小的值。于是乎我們又找到了37,然后讓37與88進行交換。有序序列就成了{35,37}。
- 再次從無序序列中選擇最小的那個值,經過查找我們找到了47,然后將47與58進行交換。此刻有序序列就成了{35, 37, 47}。
- 重復的從無序序列中選擇最小的值進行交換......
2.選擇排序具體代碼實現
有了上述選擇的示意圖,根據思路敲代碼。下方就是選擇排序的具體代碼實現。代碼實現起來還是比較簡單的,就是通過一個循環,不斷的從無序序列中選出那個最小的值與無序序列中的第一個值進行交換即可。下方第一個框中就是從無序序列中查找最小的那個值的代碼,第二個框就是交換的過程。如下所示:
3、運行結果
與上幾個排序一樣,我們輸出的運行結果就是選擇排序的詳細的過程。下方就是選擇排序的詳細過程,如下所示:
六、測試用例
在博客要結尾的部分,我們仍然會給出本篇博客所使用的測試用例。下方就是本篇博客所使用的測試用例。上述的運行結果就是下方我們測試用例的輸出結果。雖然輸出的結果不同,但是我們用的都是一個測試函數,只是傳入的排序對象不同。這也就是我們在程序的第一部分為什么要給出相應的協議定義的原因。測試用例如下所示: