算法導論之插入排序和歸並排序


  作為一名前線的碼農不時地看一下算法和數據結構還是很有必要的,雖然《算法導論》這本書很難啃,但還是有必要啃一下的。算法這東西和某種編程語言關系不大,在大學的課堂上書上一般是用偽代碼來描述算法的,而用C語言去實現。算法更多的是一種思想,一種解決問題的方法,多看看算法還是很有必要的,它可以開闊的你的思路,讓你在編程時思維更為活躍。

  當然了,本人在算法方面水平有限,這不正在努力的學習不是,接下來就按算法導論上描述的插入排序和歸並排序使用Objective-C語言實現一下,當然用什么語言是次要的,關鍵是理解算法才是關鍵。

  一、創建我們的測試工程

    因為我們只理解相應算法,沒有什么用戶圖形,也就用不到UI了,在這兒使用Xcode創建一個基於Mac開發的控制台工程即可,整個工程很簡單,一個main函數一個排序類,如下所示。

    在Sort類中我們寫了關於排序的一些類方法,然后在main函數中進行調用。

 

  二、插入排序

    插入排序顧名思義,就是把無序的元素插入到有序的元素當中。《算法導論》中舉了一個特為形象的例子,插入排序就如同你在打撲克時摸牌一樣,手里的牌是有序的,而你剛摸得牌是是隨機的,需要你插入到已經排好序的撲克牌中,這就是插入排序。

    如果用代碼實現的話就是每經過一輪插入排序后,前面有序的元素就會加一,而后面無序的元素就會減一。下面根據Demo的實例來說明一下插入排序的思路和具體實現方式。

    1.因為在OC中的可變數組是引用類型,所以在函數中改變后不需要返回。

    2.因為數組中只有一個數據的時候它就是有序的,所以前面有序數列的初始有一個數據,也就是原始數組中的第一個數據。我們從下標為1開始遍歷每個無序的元素,往前面有序的元素中相應的位置插入該元素,但插入后必須保證有序數組依然是有序的。

    3.我們需要把即將插入到有序序列的數據進行暫存,因為有序序列中大於當前要插入數據的元素需要后移,為元素插入做准備。有序元素的移動會覆蓋的要插入的元素,所以必須得暫存。

    4.遍歷有序序列,找到合適的插入位置,進行元素的插入。

 1 +(void) insertionSortWithArray: (NSMutableArray *) array{
 2     
 3     //從第二個數開始往前面的數據中進行插入,每經過一輪外面的循環,前面就插入一個從后面取出的值,
 4     //因此沒經過一輪外層循環,有序序列的長度就增加一
 5     for (int i = 1; i < array.count; i ++) {
 6         
 7         //暫存將要插入到前方的數據
 8         NSNumber *key = array[i];
 9         
10         //獲取有序序列最后一個元素的下標
11         int j = i - 1;
12         
13         //循環遍歷有序序列,尋找合適的數據插入位置,在此過程中,為數據的插入騰出位置,也就是把
14         //比將要暫存的數據大的元素向后移動
15         while (j >= 0 && array[j] > key) {
16             
17             array[j+1] = array[j];
18             
19             j--;
20         }
21         
22         //插入數據
23         array[j+1] = key;
24         
25         NSLog(@"第%d輪插入排序結果如下:", i);
26         [self displayArrayWithArray:array];
27 
28     }
29 }

     displayArrayWithArray是事先寫好的輸出數組中數據的方法,代碼如下,該方法是把數組元素拼接成字符串,然后進行輸出。

1 +(void) displayArrayWithArray: (NSMutableArray *)array{
2     
3     NSMutableString *strTemp = [NSMutableString string];
4     for (int i = 0; i < array.count; i++) {
5         [strTemp appendFormat:@"%@, ", array[i]];
6     }
7     NSLog(@"%@\n\n", strTemp);
8 }

 

    接下來,讓我們在main函數中使用隨機數產生一個隨機的數組,然后進行測試,如下:

1         //生成測試隨機數組
2         NSMutableArray *array = [[NSMutableArray alloc] init];
3         UInt count = 10;
4         for (int i = 0; i < count; i ++) {
5             NSNumber *temp =  @(arc4random()%100);
6             [array addObject:temp];
7         }

 

  進入測試階段,調用displayArrayWithArray方法,打印隨機生成的原始數組,然后調用插入排序,如下所示:

1         NSLog(@"原始數組如下:");
2         [Sort displayArrayWithArray:array];
3         
4         //插入排序
5         [Sort insertionSortWithArray:array];

 

  輸入結果如下,排序方式如下,一目了然,第一輪是前面兩個有序,第二輪是前面3個有序,以此類推,該算法的復雜度是O(n2)的

 

  三、歸並算法

    歸並算法之所以有歸並是因為把原來的問題分解成更小的子問題,然后子問題解決要比原問題更為簡單一些,把子問題的解進行有效的合並,然后得到整個問題的解。這就是分而治之的思想。

    接下來將要具體的實現歸並排序算法。

    1.首先實現歸並部分的代碼,進行歸並的數組是已經排好序了的,下面是把數組進行合並的代碼,如下:

 1 //一次歸並
 2 +(void) mergeWithArray: (NSMutableArray *)array
 3          WithStarIndex: (NSInteger) starIndex
 4           WithMidIndex: (NSInteger) midIndex
 5           WithEndIndex: (NSInteger) endIndex
 6 {
 7     //記錄歸並次數
 8     static int sort_count = 0;
 9     
10     if (endIndex < starIndex) {
11         return;
12     }
13     
14     //前半部分元素個數
15     NSInteger frontCount = midIndex - starIndex + 1;
16     
17     //后半部分元素的個數
18     NSInteger rearCount = endIndex - midIndex;
19     
20     //把數組拆分成兩部分進行歸並
21     
22     //取出前半部分
23     NSMutableArray *frontArray = [[NSMutableArray alloc] initWithCapacity:frontCount];
24     for (NSInteger i = 0; i < frontCount; i ++) {
25         [frontArray addObject:array[starIndex + i]];
26     }
27     
28     //取出后半部分
29     NSMutableArray *rearArray = [[NSMutableArray alloc] initWithCapacity:rearCount];
30     for (NSInteger i = 0; i < rearCount; i ++) {
31         [rearArray addObject:array[midIndex + i + 1]];
32     }
33     
34     
35     //進行比較歸並
36     
37     NSInteger fi = 0;
38     NSInteger ri = 0;
39     NSInteger oi = starIndex;
40     
41     //當兩個子數組中都有元素時才進行合並
42     while (fi < frontArray.count && ri < rearArray.count) {
43         
44         if(frontArray[fi] <= rearArray[ri]){
45             
46             array[oi++] = frontArray[fi++];
47             continue;
48         }
49         
50         array[oi++] = rearArray[ri++];
51     }
52 
53     //前面元素中經過合並后仍然有元素,把剩余的元素進行添加
54     while (fi < frontArray.count) {
55         
56         array[oi++] = frontArray[fi++];
57         
58     }
59 
60     //后邊元素經過合並后仍然有元素,把剩余元素進行添加
61     while (ri < rearArray.count) {
62         
63         array[oi++] = rearArray[ri++];
64         
65     }
66     
67     NSLog(@"第%d合並結果如下:", ++ sort_count);
68     [self displayArrayWithArray:array];
69 }

 

    上面的代碼只是進行問題解的合並,下方是對問題進行拆分,分解成規模比較小的子問題,遞歸分解代碼如下,在這就不多說了,下面代碼中已經給出了注釋。

 1 #pragma mark -- 本方法是把問題進行遞歸分割,使其成為多個相似的子問題,然后在把子問題進行合
 2 +(void) mergeSortWithArray: (NSMutableArray *)array
 3              WithStarIndex: (NSInteger) starIndex
 4               WithEndIndex: (NSInteger) endIndex
 5 {
 6     //遞歸結束條件
 7     if (starIndex >= endIndex) {
 8         return;
 9     }
10     
11     //找出中點進行分解
12     NSInteger midIndex = (starIndex + endIndex)/2;
13     
14     //遞歸分解前半部分
15     [self mergeSortWithArray:array WithStarIndex:starIndex WithEndIndex:midIndex];
16     
17     //遞歸分解后半部分
18     [self mergeSortWithArray:array WithStarIndex:midIndex + 1 WithEndIndex:endIndex];
19     
20     //經過上面的遞歸分解后,最小的子數組里只有一個元素,也就是有序的了,然后從底層進行遞歸合並
21     [self mergeWithArray:array WithStarIndex:starIndex WithMidIndex:midIndex WithEndIndex:endIndex];
22     
23 }

  

  調用歸並排序代碼如下:

1         //歸並排序
2         [Sort mergeSortWithArray:array WithStarIndex:0 WithEndIndex:array.count-1];

 

  運行結果如下,仔細觀察每次歸並后的結果,你會找到規律的哦。

  今天的博客就先到這吧,編程是少不了算法的呀,繼續努力學習中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM