經常使用排序算法(五)基數排序、桶排序以及計數排序

這是三種線性時間複雜度的排序算法,它們是用運算而不是比較來肯定排序順序的

1、基數排序

1.簡介

它一種與其餘排序算法徹底不一樣的排序方法,其餘的排序算法都是經過關鍵字之間的比較和移動來完成的,而它是採用一種多關鍵字的思想。算法

多關鍵字的思想:給定一組數據,我能夠先按個位的大小對全部數進行排序,而後再按十位進行排序,一直到最高位,這樣就可使整組數據變得有效,這樣從最低位開始的方法稱爲最低位優先數組

2.圖解過程

通過一次放置和回收,獲得的序列已經按個位有序了,接下來按照次低位再次進行放置和回收。分佈式

由此能夠看出,若是一組序列中最大的數爲兩位數,則須要兩次的分配和收集,整個分配收集的次數與最大數的位數有關性能

基數排序須要兩個輔助空間,一個是0~9號桶,另外一個是計算定位的數組,定位數組是幹什麼的呢?就是記錄每一個桶中的數據待會要放回原數組的哪一個位置。大數據

 

def radixSort(number: Array[Int], d: Int): Unit = { //d表示最大的數有多少位
    var k = 0
    var n, m = 1
    //控制鍵值排序依據在哪一位
    val temp = Array.ofDim[Int](10, number.length)
    //數組的第一維表示可能的餘數0-9
    val order = new Array[Int](10) //數組orderp[i]用來表示該位是i的數的個數
    while (m <= d) {
      for (i <- number.indices) {
        val lsd = (number(i) / n) % 10
        temp(lsd)(order(lsd)) = number(i)
        order(lsd) += 1
      }
      var i = 0
      while (i < 10) {
        if (order(i) != 0) {
          var j = 0
          while (j < order(i)) {
            number(k) = temp(i)(j)
            k += 1
            j += 1
          }
        }
        order(i) = 0
        i += 1
      }
      n *= 10
      k = 0
      m += 1
    }
  }

基數排序的時間複雜度能夠理解爲O(d*n),d爲序列中最大的位數,適用於n值很大,可是關鍵字較小的序列。scala

2、桶排序

1.概念

桶排序將[0,1)區間劃分爲n個相同的大小的子區間,這些子區間被稱爲桶。而後將n個輸入元素分別放入各自的桶中。由於輸入時均勻獨立的,因此通常不會有不少數同時落在一個桶中的狀況。這樣,咱們想對各個桶中的數據進行排序,而後遍歷每一個桶,按照次序把各個桶中的元素列出來便可blog

2.特性說明

1》. 桶排序的時間複雜度一般是O(N+N*logM),其中,N表示桶的個數,M表示桶內元素的個數(這裏,M取的是一個大概的平均數,這也說明,爲什麼桶內的元素儘可能不要出現有的不少,有的不多這種分佈不均的事情,分佈不均的話,算法的性能優點就不能最大發揮)。排序

2》. 桶排序是穩定的(是能夠作到平衡排序的)。內存

3》. 桶排序,在內存方面消耗是比較大的,能夠說其時間性能優點是由犧牲空間換來的。input

桶排序,在大數據量的狀況下排序,比快速排序還要快。若待排序的數據元素個數比較少,桶排序的優點就不是那麼明顯了,由於桶排序就是基於分而治之的策略,能夠將數據進行分佈式排序,充分發揮並行計算的優點。

3.步驟

1》.找出待排序數組中的最大值max、最小值min

2》.咱們使用 動態數組ArrayList 做爲桶,桶裏放的元素也用 ArrayList 存儲。桶的數量爲(max-min)/arr.length+1

3》.遍歷數組 arr,計算每一個元素 arr[i] 放的桶

4》.每一個桶各自排序

5》.遍歷桶數組,把排序好的元素放進輸出數組

4.實現

def bucketsort(inputData: ArrayBuffer[Int], max: Int): ArrayBuffer[Int] = {
    var buckets = new Array[Int](max)
    for (i <- inputData.indices) //計數
      buckets(inputData(i)) = buckets(inputData(i)) + 1
    var j = 0
    for (i <- 0 until max)
      while (buckets(i) > 0) {
        inputData(j) = i
        j = j + 1
        buckets(i) = buckets(i) - 1
      }
    buckets = null
    inputData
  }

3、計數排序

1.優點

計數排序是桶排序的一種特殊狀況,能夠把計數排序當成每一個桶裏只有一個元素的狀況,它是一個非基於比較的排序算法,它的優點在於在對必定範圍內的整數排序時,它的複雜度爲Ο(n+k)(其中k是整數的範圍),快於任何比較排序算法。

2.思想

對於一個輸入數組中的一個元素x,若是這個數組中比x小的元素有n個,那麼咱們就能夠直接把x放到(n+1)的位置上。這就是計數排序的基本思想,相似於哈希表中的直接定址法,在給定的一組序列中,先找出該序列中的最大值和最小值,從而肯定須要開闢多大的輔助空間,每個數在對應的輔助空間中都有惟一的下標。
基於這個思想,計數排序的一個主要問題就是如何統計數組中元素的個數。再加上輸入數組中的元素都是0-k區間的一個整數這個條件,那麼就能夠經過另一個數組的地址表示輸入元素的值,數組的值表示元素個數的方法來進行統計。

下面給出統計數組元素都是0-k區間的整數的數組中各個元素個數的方法。

  1. 找出序列中最大值和最小值,開闢Max-Min+1的輔助空間
  2. 最小的數對應下標爲0的位置,遇到一個數就給對應下標處的值+1,。
  3. 遍歷一遍輔助空間,就能夠獲得有序的一組序列

 

3.算法分析: 

計數排序是一種以空間換時間的排序算法,而且只適用於待排序列中全部的數較爲集中時,好比一組序列中的數據爲0 1 2 3 4 999;就得開闢1000個輔助空間。 
時間複雜度 
計數排序的時間度理論爲O(n+k),其中k爲序列中數的範圍。 
不過當O(k)>O(n*log(n))的時候其效率反而不如基於比較的排序(基於比較的排序的時間複雜度在理論上的下限是O(n*log(n)), 如歸併排序,堆排序)

4.代碼實現

def Countingsort(inputData: ArrayBuffer[Int], k: Int): Array[Int] = {
    //k表示有所輸入數字都介於0到k之間
    val temp = new Array[Int](k)
    // 臨時存儲區
    val outdata = new Array[Int](inputData.length)
    val len = temp.length
    for (i <- 0 until len) {
      // 初始化
      temp(i) = 0
    }
    for (i <- inputData.indices) {
      temp(inputData(i)) = temp(inputData(i)) + 1
    }
    for (i <- 1 until len) {
      temp(i) = temp(i) + temp(i - 1)
    }
    // 把輸入數組中的元素放在輸出數組中對應的位置上
    var n = inputData.length - 1
    while (n >= 0) {
      // 從後往前遍歷
      outdata(temp(inputData(n)) - 1) = inputData(n)
      temp(inputData(n)) = temp(inputData(n)) - 1
      n = n - 1
    }
    outdata
  }
相關文章
相關標籤/搜索