數據結構與算法(三)—— 常見排序算法和swift實現

目錄

  1. 冒泡排序
  2. 插入排序
  3. 選擇排序
  4. 堆排序
  5. 歸併排序
  6. 快速排序
  7. 計數排序
  8. 桶排序

冒泡排序

原理:算法

  1. 比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。
  2. 對每一對相鄰元素作一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
  3. 針對全部的元素重複以上的步驟,除了最後一個。
  4. 持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較

時間複雜度:O(n^2)數組

///冒泡函數代碼實現
func bubbleSort(_ arr: inout [Double]) {
    for i in 0 ..< arr.count {
        for j in 0 ..< arr.count - i {
            if j+1 < arr.count {
                if arr[j] > arr[j+1] {
                    let temp = arr[j]
                    arr[j] = arr[j+1]
                    arr[j+1] = temp
                    
                }
            }
           
        }
    }
}
複製代碼

插入排序

原理:bash

  1. 從第一個元素開始,該元素能夠認爲已經被排序
  2. 取出下一個元素,在已經排序的元素序列中從後向前掃描
  3. 若是該元素(已排序)大於新元素,將該元素移到下一位置
  4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
  5. 將新元素插入到下一位置中
  6. 重複步驟2~5

若是比較操做的代價比交換操做大的話,能夠採用二分查找法來減小比較操做的數目。該算法能夠認爲是插入排序的一個變種,稱爲二分查找排序。數據結構

時間複雜度:O(n^2)app

///插入排序的實現
func insertSort(_ arr: inout [Double]) {
    //從第1位開始做爲要插入的數
    for i in 1 ..< arr.count  {
        let temp = arr[i]
        var j = i - 1
    
        while (j >= 0 && temp < arr[j]) {
            //與已排序的數逐一比較,大於temp時,該數移後
            arr[j + 1] = arr[j]
            j -= 1
        }
        if j != i - 1 {
            //在相應的位置插入(由於上面j-1跳出while循環,因此這裏是j+1)
            arr[j + 1] = temp
        }
       
    }
}

複製代碼

選擇排序

原理:函數

對比數組中前一個元素跟後一個元素的大小,若是後面的元素比前面的元素小則用一個變量k來記住他的位置,接着第二次比較,前面「後一個元素」現變成了「前一個元素」,繼續跟他的「後一個元素」進行比較若是後面的元素比他要小則用變量k記住它在數組中的位置(下標),等到循環結束的時候,咱們應該找到了最小的那個數的下標了,而後進行判斷,若是這個元素的下標不是第一個元素的下標,就讓第一個元素跟他交換一下值,這樣就找到整個數組中最小的數了。而後找到數組中第二小的數,讓他跟數組中第二個元素交換一下值,以此類推ui

時間複雜度:O(n^2) ,不穩定spa

選擇排序的交換操做介於 0 和 (n - 1) 次之間。選擇排序的比較操做爲 n (n - 1) / 2 次之間。選擇排序的賦值操做介於 0 和 3 (n - 1) 次之間。 比較次數O(n^2),比較次數與關鍵字的初始狀態無關,總的比較次數N=(n-1)+(n-2)+...+1=n*(n-1)/2。交換次數O(n),最好狀況是,已經有序,交換0次;最壞狀況交換n-1次,逆序交換n/2次。交換次數比冒泡排序少多了,因爲交換所需CPU時間比比較所需的CPU時間多,n值較小時,選擇排序比冒泡排序快。設計

///選擇排序
func selectionSort(_ arr: inout [Double]) {
    
    for i in 0 ..< arr.count-1 {
        
        var min = i
        //找出最小值的位置
        for j in i+1 ..< arr.count {
            if arr[min] > arr[j] {
                min = j
            }
        }
        //若是最小值的下標不是i,就交換
        if min != i {
            let temp = arr[i]
            arr[i] = arr[min]
            arr[min] = temp
        }

    }
    
}

複製代碼

堆排序

堆排序是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似徹底二叉樹的結構,並同時知足堆積的性質:即子結點的鍵值或索引老是小於(或者大於)它的父節點。code

原理:

  1. 將無序列構建成一個堆,根據升序降序需求選擇大頂堆或小頂堆;
  2. 將堆頂元素與末尾元素交換,將最大元素"沉"到數組末端;
  3. 從新調整結構,使其知足堆定義,而後繼續交換堆頂元素與當前末尾元素,反覆執行調整+交換步驟,直到整個序列有序。

   

時間複雜度:O(nlogn),不穩定

/*
 堆排序
 */

//調整一個根節點和左右節點的位置
func adjustHeap(arr:inout [Double],i: Int,length: Int) {

    let temp = arr[i]
    var tempNum = i
    var left = 2 * i + 1

    while left < length {
        
        ///若是右節點存在,且大於左節點,取右節點
        if left+1 < length && arr[left + 1] > arr[left]{
            left += 1
        }
        ///若是父節點已經大於子節點
        if temp >= arr[left] {
            break
        }
        
        arr[tempNum] = arr[left]
        tempNum = left
        left = 2*left + 1
    }
    arr[tempNum] = temp
}

//互換數組元素
func swapArr(arr: inout [Double],index1: Int,index2:Int) {
    let temp = arr[index1]
    arr[index1] = arr[index2]
    arr[index2] = temp
}

///堆排序
func heapSort(_ arr: inout [Double]) {
    
    ///建立初始化堆
    var i = arr.count/2
    while i >= 0 {
        adjustHeap(arr: &arr, i: i, length: arr.count)
        i -= 1
    }
    
    var j = arr.count - 1
    while j > 0 {
        swapArr(arr: &arr, index1: 0, index2: j)
        adjustHeap(arr: &arr, i: 0, length: j)
        j -= 1
    }
    
    
}


複製代碼

歸併排序

原理:

將無序的序列分紅子序列,將子序列排序在合成有序的序列。

例如:初始狀態:6,202,100,301,38,8,1

  1. 第一次歸併後:{6,202},{100,301},{8,38},{1}
  2. 第二次歸併後:{6,100,202,301},{1,8,38}
  3. 第三次歸併後:{1,6,8,38,100,202,301}

時間複雜度:O(nlogn)

///歸併排序的實現
func mergeSort(_ arr: inout [Double]) {
    var temp = Array<Double>(repeating:0, count: arr.count)
    helperSort(arr: &arr, left: 0, right: arr.count-1,temp: &temp)
}

///分
func helperSort(arr: inout [Double], left: Int, right: Int, temp: inout [Double]) {
    
    if left < right {
        let mid = (left + right)/2
        ///遞歸分數組左邊元素
        helperSort(arr: &arr, left: left, right: mid,temp: &temp)
        ///遞歸分數組右邊元素
        helperSort(arr: &arr, left: mid+1, right: right,temp: &temp)
        ///遞歸合併子數組
        merge(arr: &arr, left: left, right: right, mid: mid, temp: &temp)
    }
}

///合
func merge(arr: inout [Double], left: Int, right: Int, mid: Int, temp: inout [Double]) {
    var i = left //
    var j = mid + 1
    var t = 0 //臨時數組下標
    
    ///把左邊和右邊的元素按順序放進temp
    while i <= mid && j <= right {
        if arr[i] <= arr[j] {
            temp[t] = arr[i]
            t += 1
            i += 1
        }else {
            temp[t] = arr[j]
            t += 1
            j += 1
        }
    }
    
    ///將左邊剩餘元素放進temp中
    while i <= mid {
        temp[t] = arr[i]
        t += 1
        i += 1
    }
    //將右邊剩餘元素放進temp中
    while j <= right {
        temp[t] = arr[j]
        t += 1
        j += 1
    }


    //將temp中的元素所有拷貝到原數組中
    i = left
    t = 0
    while i <= right {
        arr[i] = temp[t];
        i += 1
        t += 1
    }
}


複製代碼

快速排序

原理:

已知數組:A[n]

  1. 設置兩個變量i、j,排序開始的時候:i = 0,j = N-1
  2. 以第一個數組元素做爲關鍵數據,賦值給key,即key = A[0]
  3. 從j開始向前搜索,即由後開始向前搜索(j--),找到第一個小於key的值A[j],將A[j]的值賦給A[i]
  4. 從i開始向後搜索,即由前開始向後搜索(i++),找到第一個大於key的A[i],將A[i]的值賦給A[j] 5.重複第三、4步,直到i = j

時間複雜度:O(nlogn)

///快速排序的實現
///大於基數的數放在右邊,小於基數的數放在左邊
func quicklySort(arr: inout [Double], left: Int, right: Int) {
    
    if left >= right {
        return
    }
    
    let num = arr[left] //取第一個數做爲基數
    var l = left
    var r = right
    
    while l < r {
        //從右邊遍歷
        while l < r && arr[r] >= num{
            r -= 1
        }
        arr[l] = arr[r]
       
        while l < r && arr[l] <= num{
            l += 1
        }
        arr[r] = arr[l]

    }
    
    arr[l] = num

    quicklySort(arr: &arr, left: left, right: l - 1)
    quicklySort(arr: &arr, left: l + 1 , right: right)
    
}

複製代碼

計數排序

原理:

首先要排序的數都是整數

  1. 找出最大值max和最小值min
  2. 建立一個大小爲max-min+1的數組Temp,每一個元素的值都爲0
  3. 遍歷數,找到Temp對應的下標的元素加1,即統計數出現的次數
  4. 經過遍歷Temp,根據統計的每一個數的次數,相對應的排序

計數排序是一種非比較類型的算法,因此沒有O(nlogn)的下限,是經過犧牲空間換時間的算法。

時間複雜度:O(n)

//計數排序的實現
func countSort(_ arr: inout [Int]) {
    if arr.count <= 1 {
        return
    }
    //找出最大值和最小值
    var max = arr[0]
    var min = arr[0]
    for num in arr {
        if num >= max {
            max = num
        }
        
        if min >= num{
            min = num
        }
    }
    let len = max - min + 1
    var temp = Array<Int>(repeatElement(0, count: len))
    //統計每一個數出現的次數
    for num in arr {
        temp[num - min] = temp[num - min] + 1
    }
    print(temp)
    var nextIndex = 0;
    for  i  in  0 ..< temp.count {
        var val = temp[i]
        while val > 0  {
            arr[nextIndex] = i + min;
            nextIndex = nextIndex + 1
            val = val - 1
        }
    }

    
}


複製代碼

桶排序

原理:

  1. 找出最大值max和最小值min
  2. 給定一個桶的數量,建立一個桶數組(二維數組),每一個桶的範圍爲(max-min+1)/桶的數量
  3. 遍歷數,把數值添加到對應範圍的桶中,若是桶中有其餘數值,按要求排序
  4. 經過遍歷桶數組,獲得一個順序的數組

時間複雜度:O(n)

///桶排序
///bucketCount是桶的大小
func bucketSort(_ arr: inout [Double], bucketCount:Int) {
 
    if arr.count <= 1 || bucketCount <= 0 {
        return
    }
    //找出最大值和最小值
    var max = arr[0]
    var min = arr[0]
    for num in arr {
        if num >= max {
            max = num
        }
        
        if min >= num{
            min = num
        }
    }
    
    //每一個桶的數值範圍
    let space = (max - min + 1) / Double(bucketCount)
    var buckets = Array<Array<Double>>(repeating: [Double](), count: bucketCount)
    //將數值放到對應範圍的桶中
    for i in 0 ..< arr.count {
        ///數值對應的桶
        let index = Int((arr[i] - min) / space)
        //
        if buckets[index].isEmpty {
            buckets[index].append(arr[i])
        }else {
            ///從小到大排序
            for j in 0 ..< buckets[index].count {
                if arr[i] > buckets[index][j] {
                    buckets[index].insert(arr[i], at: j + 1)
                }
            }
          
        }
        
    }
    
    ///排序全部的桶
    var n  = 0
    var index = 0
    while n < buckets.count {
        let bucket = buckets[n]
        if !bucket.isEmpty {
            arr.insert(contentsOf: bucket, at: index)
            index += bucket.count
        }
        n = n + 1
    }
    
    
}

複製代碼
相關文章
相關標籤/搜索