排序算法(Swift)

  • 冒泡排序:它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果他們的順序(如從大到小、首字母從A到Z)錯誤就把他們交換過來。走訪元素的工作是重複地進行直到沒有相鄰元素需要交換,也就是說該元素列已經排序完成。時間複雜度: n(n-1)/2 = O(n²)

計算機科學中,算法的時間複雜度是一個函數,它定性描述了該算法的運行時間。這是一個關於代表算法輸入值的字符串的長度的函數。時間複雜度常用大O符號表述,不包括這個函數的低階項和首項係數。使用這種方式時,時間複雜度可被稱爲是漸近的,它考察當輸入值大小趨近無窮時的情況。

    func bubbleSort(_ arr : Array<Int>) -> Array<Int> {
        var array = arr
        var num = 0
        //理論上i應該小於array.count-1,因爲n個元素只需要進行n-1輪循環比較就能確定n-1個元素,剩下的哪一個元素無需比較就已經確認啦.但是由於內部條件有限制,實際上第n輪循環時i=n-1,這時內部的循環就不再進行啦,所以也不會有問題,但不夠嚴謹.
        for i in 0..<array.count {
            //每完成一次內部循環,就會獲取當前循環中的最大值,放在最後位,下次就不用再比較,這就是-i的原因
            for j in 0..<array.count-1-i {
                if array[j] > array[j+1] { //如果是倒敘就<,正序就>
                    array.swapAt(j, j+1)
//                    let temp = array[j+1]
//                    array[j+1] = array[j]
//                    array[j] = temp
//                    print(array)
                }
                num = num + 1
                print(num)
            }
        }
        return array
    }
  • 選擇排序 : Selection sort是一種簡單直觀的排序算法。它的工作原理是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到全部待排序的數據元素排完。 選擇排序是不穩定的排序方法。

    選擇排序算法十分簡單易懂,但是它的效率比較低,是O(n^2)。它比插值排序更低效但比冒泡排序更高效。在未排序數組中尋找最小值的過程很慢,而這個過程又是不斷被重複的。

//選擇排序
func selectionSort(_ arr : Array<Int>) -> Array<Int>{
    var array = arr;
    //有n個數字只需要循環n-1輪就能確定n-1個數字的位置,那麼剩下的那一個也就確定了
    for i in 0..<array.count-1{
        //m是用來獲得最小(大)數的腳標
        var m = i
        for j in i+1..<array.count{
            if array[j] < array[m]{ //小於號就每次循環獲得最小數,大於號就是每次循環獲得最大數.
                m = j
                print(m)
            }
        }
        //m是每輪內部循環後標記最小數的腳標,i是要插入的順序,如果不等就說明該輪需要替換兩個數的位置.如果相等就說明當前循環最前的就是最小的數字,不需要替換位置.
        if i != m{
            array.swapAt(i, m)
            print(array)
        }
    }
    return array;
}

關鍵點在於m是標記每輪最小數的腳標,i是當前已經排序的當前內部循環獲得最小數要插入的位置(當前輪最小腳標).

  • 插入排序 : Insertion sort是一種簡單直觀且穩定的排序算法.選取一個數字,並將其插入在數組裏正確的排序位置。比如一個無序的數組A,我先取出第一個元素放入空數組B中,再選取第二個元素,如果比B中元素大就放在後面,否則放在前面,第三次就取A中的第三個元素,和B中的第一個元素比較,如果小就插入到最前面,如果大就和B中的第二個元素比較,如果小就插入到B中的第一個元素和第二個元素之間,如果大就插入到B中的第2個元素後面,依次類推.這個爲了便於理解分成了兩個數組,其實可以是一個數組,只是要標記哪一部分是已經排序過的,哪一部分是爲排序的.具體可參考插入排序. 最壞情況下插入排序的平均情況性能是 O(n ^ 2) (最壞的情況就相當於冒泡排序)
    插入排序
func insertionSort(_ arr: [Int]) -> [Int] {
   var array = arr
   // i相當於分割,即將排序的那一個,0-x是已經排序的d,0-count-1是未排序的,因爲第一個元素移過去不需要比較排序所以從1開始,還是需要n-1次外部循環
   for i in 1..<array.count {
       var index = i       
  //在while循環之前index是未排序的,index-1及以前都是已經排序後的,循環之後index位就是已排序的了,因爲是從後往前比,一但找到合適的位置(array[index] < array[index - 1]),就不在會繼續往下比較了,就插入進去(array.swapAt(index - 1, index))
       while index > 0 && array[index] < array[index - 1] {  //小於號是正序排列,大於號是倒序排列
           array.swapAt(index - 1, index)
           print(array)
           //防止數組取值腳標越界,-1是不能爲數組腳標的
           index -= 1
       }
   }
   return array
}
  • 折半查找: 也叫折半搜索(英語:half-interval search),又稱二分搜索(英語:binary search)、對數搜索(英語:logarithmic search),是一種在有序數組中查找某一特定元素的搜索算法。搜索過程從數組的中間元素開始,如果中間元素正好是要查找的元素,則搜索過程結束;如果某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,而且跟開始一樣從中間元素開始比較。如果在某一步驟數組爲空,則代表找不到。這種搜索算法每一次比較都使搜索範圍縮小一半.
func binarySearch<T: Comparable>(_ a: [T], key: T) -> Int? {
    //小腳標
    var lowerBound = 0
    //大腳標
    var upperBound = a.count
    //循環查找的條件是小腳標必須小於大腳標
    while lowerBound < upperBound {
        //取中間腳標 lowerBound + (upperBound - lowerBound) / 2
        let midIndex = (lowerBound+upperBound)/2
        //如果中間腳標的值等於目標值,則返回該腳標
        if a[midIndex] == key {
            return midIndex
        //如果中間腳標的值比目標值小,則將最小腳標設置爲比現在的中間腳標大1,然後進行下一次循環
        } else if a[midIndex] < key {
            lowerBound = midIndex + 1
        //如果中間腳標的值比目標值大,則將最大腳標設置爲現在的中間腳標,爲什麼不減1,因爲這樣就可能出現找不到邊界值的情況,比如a = [1,22],key = 1,這時候如果是upperBound = midIndex- 1返回的就是nil
        } else {
            upperBound = midIndex
        }
    }
    return nil
}

注意:這邊查找的前提是數組已經是有序的,並且不能有重複數字.

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