国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript各種排序算法的實現及其速度性能分析

yuanxin / 3548人閱讀

摘要:今天我們來討論的問題有兩個如何用實現選擇排序冒泡排序插入排序快速排序歸并排序堆排序對生成的萬個隨機數進行排序,各個排序算法的性能分析??焖倥判蚩焖倥判蛩惴ɑ旧鲜敲嬖嚤乜寂判蛩惴?,也是傳聞最好用的算法。

今天我們來討論的問題有兩個:

如何用JavaScript實現選擇排序、冒泡排序、插入排序、快速排序、歸并排序、堆排序;

對生成的10萬個隨機數進行排序,各個排序算法的性能分析。

創建數據類型

這里我們全部用數組來存儲數據,首先創建一個類ArrayList。
其中屬性的說明如下:

array空數組--->用以存放數據

insert()方法--->往array中插入數據

swapItemInArray(n,m)方法--->將array中第n個元素和第m個元素交換位置

toString()方法--->將array數組轉換為字符串

originSort()方法--->JavaScript原生排序算法實現,在之后的性能比較中,我們會用到它

function ArrayList(){
  var array = []
  this.insert = function(item){
    array.push(item)
  }
  this.swapItemInArray = function(n,m){
    let temp = array[n]
    array[n] = array[m]
    array[m] = temp
  }
  this.toString = function(){
    return array.join()
  }
  this.originSort = function(){
    array.sort(function(a,b){
      return a-b
    })
  }
}
選擇排序

先來實現最簡單的選擇排序。
其思路是:對于有N個數字的數組,進行N輪排序,在每一輪中,將最大的數找出,放到末尾。下一輪的時候再找出次大的數放到倒數第二位。
我們來為ArrayList類添加如下方法

  this.selectSort = function(){
    var self = this
    var len = array.length
    var maxIndex
    for (var i = 0; i < len; i++) {
      maxIndex = 0   //初始化最大數的位置
      for (var j = 0; j < len - i; j++) {
        if (array[maxIndex] < array[j]) {  //每一次都和之前的最大數比較
          maxIndex = j    //如果大于之前的最大數,則紀錄當前數為最大數
        }
      }
      //第i輪結束后,將最大數放到數組倒數第i個
      self.swapItemInArray(maxIndex,len-i-1)  
    }
  }
冒泡排序

選擇排序是不是太簡單了?接下來我們就來實現冒泡排序。
思路:對于有N個數字的數組,進行N輪排序,在每一輪中,從前往后以此比較兩兩相鄰的數字,每次比較后,都把大的往后放,一輪下來,最大的數會被推到數組最后。

  this.bubbleSort = function(){
    var self = this
    var len = array.length
    for (var i = 0; i < len; i++) {     //數組長度要遍歷的趟數
     //第i趟之后,后面i個元素都不用比較
      for (var j = 0; j < len - 1 - i; j++) {     
        if (array[j]>array[j+1]) {  //兩兩相鄰進行比較
          self.swapItemInArray(j,j+1)  //將較大的數字放到后面
        }
      }
    }
  }
插入排序

插入排序的實現思路如下:
對于有N個數字的數組,進行N輪排序

第一輪 將第2個數字與第1個數字比較,如果第2個數字小,則與1交換

第二輪 將第3個數字與第2個數字比較
如果第3個數字小,則與第2個數字交換,再用第2個數字與第1個數字比較,將小的放前面。

第i輪 第1個到第i-1個數字已經全是從小到大排列了,第i個數字與前面的數字依次比較并交換位置,使得第1個數字,到第i個數字也是從小到達排列。

代碼如下

  this.insertSort = function(){ 
    var self = this
    var len = array.length
    for (var i = 0; i < len; i++) {
      for (var j = i; j>0; j--) {
        if (array[j]

上面幾種排序的實現是不是很小兒科呢?下面的就要稍微復雜點了。

快速排序

快速排序算法基本上是面試必考排序算法,也是傳聞最好用的算法。
不過實現起來可一點都不容易,至少對我來說是這樣。
算法思想
本質上快速排排序是一種分治算法的實際應用。
按照下圖所示,對于左邊的原始數集合,(隨便地)取一個數(稱其為主元),比如取65為主元,則65則將原來的集合劃分為了A集合和B集合,A中所有的數字都小于65,B中所有的數字都大于65。
然后。
之后再對A集合和B集合采取相同方式的劃分。
最后就分為了從小到大排列的眾多小集合。

實現思路
對于有N個數字的數組,進行大約logN輪的排序。
若每次都能劃分為兩等份,則效率最高。如果選擇的那個數將數組劃分為了1、N-1長度的兩個數組則效率會非常低。
因此,主元的選擇非常關鍵。不能用JavaScript中所提供的Math.random()獲得主元,由該函數生成隨機數代價昂貴。
根據相關資料,一個比較好的方法為:取首項、中間項、末尾項中的中值作為劃分基準。

主元的具體實現函數如下
它傳入三個參數,數組本身、首項索引、尾項索引。
在查找中值的時候,順便將三個值分別排列成,首項最小、中位數中等、尾項最大。
為了方便后續的劃分,將主元和倒數第二個數進行交換(由于尾項已經大于中值,因此不必對其進行操作,故主元放到倒數第二個)

    function findPivot(arr,left,right){//主元取中位數
      var center = Math.floor((left+right)/2)
      if (arr[left] > arr[center]) {
        self.swapItemInArray(left,center)
      }
      if (arr[left] > arr[right]) {
        self.swapItemInArray(left,right)
      }
      if (arr[center] > arr[right]) {
        self.swapItemInArray(center,right)
      }
      self.swapItemInArray(center,right-1)//主元被藏在倒數第二個
      return arr[right-1]
    }

每趟如何劃分?
以下圖為例,“9”為主元,我們把它放在最后一個。
這里設置i和j兩個指針(JavaScript中則是數組下標),i指向首項“2”,j指向倒數第2個數字“7”。
讓i指針往右邊移動,遇到>=主元“9”的項時停下來
讓j指針往左邊移動,遇到<=主元“9”的項時停下來
交換i和j所指的值,并且i右移一位,j左移一位
i指針和j指針繼續移動比較交換
當i與j發生交錯時,本趟劃分結束,把主元與i所指的“6”進行交換(即把主元放回原位)。此時數組被劃分為了兩個[0,...,j]、[i+1,...,last]。[0,...,j]中的所有元素都小于主元,[i+1,...,last]中所有元素都大于主元。
對劃分出來的兩個子數組繼續進行下一步劃分。

具體函數實現如下,由于數組長度太小時采用快速排序效率較低,因而當數組長度太小時,我們改用插入排序。

    function partition(arr,left,right){ //分割操作
      var pivot = findPivot(arr,left,right) //找到主元
      var length = right - left
      if (length>cutoff) { //當劃分組沒有小于閾值時,繼續采用快速排序
        var i = left
        var j = right - 2 
        while(i<=j){ //i和j沒有交錯
          while(arr[i]pivot){
            j--
          }
          if (i<=j) {
            self.swapItemInArray(i,j)
            i++
            j--
          }
        }
        self.swapItemInArray(i,right-1)  //結束后將主元放回原位
        if (left

快速排序的完整代碼

  this.quickSort = function(){
    var self = this
    var cutoff = 3

    function partition(arr,left,right){ //分割操作
      var pivot = findPivot(arr,left,right)
      var length = right - left
      if (length>cutoff) {
        var i = left
        var j = right - 2
        while(i<=j){
          while(arr[i]pivot){
            j--
          }
          if (i<=j) {
            self.swapItemInArray(i,j)
            i++
            j--
          }
        }
        self.swapItemInArray(i,right-1)
        if (left arr[center]) {
        self.swapItemInArray(left,center)
      }
      if (arr[left] > arr[right]) {
        self.swapItemInArray(left,right)
      }
      if (arr[center] > arr[right]) {
        self.swapItemInArray(center,right)
      }
      self.swapItemInArray(center,right-1)//主元被藏在倒數第二個
      return arr[right-1]
    }
    function insertSort(left,right){ //當分塊足夠小的時候,用插入排序
      var len = right - left
      for (var i = 0; i <= len; i++) {
        for (var j = i; j > 0; j--) {
          if (array[j]
歸并排序

與快速排序的“在劃分中排序”不同,歸并排序的基本思想是先將長度為N的數組劃分為N個長度為1的數組,然后兩兩合并,在合并的時候排序。

如何在兩個子數組歸并的時候排序
如下圖,對于A數組和B數組,設置指針Aptr和指針Bptr,它們的初始位置都在倆數組的首部。
將Aptr和Bptr所指的數對比,將小的數放到C數組中。
比如Aptr所指“1”,Bptr所指“2”,Aptr所指的“1”小,則將“1”放入到C中,Aptr后移,Bptr不動。
再對比Aptr所指的“13”和Bptr所指的“2”,“2”較小,將其推入到C中,Bptr右移,Aptr不動。
反復重復上述操作,如果最后一個數組A空,另一個數組B還有剩余元素,則依次將數組B的剩余元素全部放到C中。
至此完成依次歸并操作。

代碼實現為

     function merge(arr1,arr2){
      var i = 0
      var j = 0
      var tempArr = []
      while(i

歸并排序的完整代碼如下,我們這里采用遞歸來實現劃分

  this.mergeSort = function(){
     function merge(arr1,arr2){
      var i = 0
      var j = 0
      var tempArr = []
      while(i
堆排序

最大堆是什么
最大堆是一個完全二叉樹。
但它還滿足,任意一個節點,它的值大于左子樹中的任意元素的值,也大于右子樹中的任意元素值。
該節點的左子樹元素的值和右子樹元素的值的大小沒有要求。

堆的數組表示
當我們用數組(從0開始)來表示一個堆的時候,第i個元素的左子元素為第(2*i+1)個元素,右子元素為第(2i*2)個元素。

堆排序的大致思想
將數組按最大堆的方式排列,比如排列為[a,b,c,d]
將一個最大堆的根(a)和最后一個元素(d)交換,
把數組中除了最后一個數(a)以外的元素[d,b,c]重新調整為最大堆。
對[d,b,c]重復上述操作

如何創建最大堆
把所有元素插入到數組array(設長度為N)中后,從索引為(Math.floor(N/2) - 1)的元素開始,依次向前地將它和它的子樹調整為最大堆。
如下圖,先將子樹①調整為最大堆 ----> 調整子樹②為最大堆 ----> 調整整個樹③為最大堆

最大堆創建的代碼如下

    function createMaxHeap(){  //創建最大堆
      var len = array.length
      var startIndex = Math.floor(len/2) - 1 //從這個節點開始,將其子樹調整為最大堆
      for (var i = startIndex; i >= 0; i--) {
        compareChildAndAdjust(i)
      }
    }
    function compareChildAndAdjust(i,lastIndex){
      var bigChildIndex = findBigInChildren(i,lastIndex)
      if (bigChildIndex==false) {  //當找到的子節點返回為false時,表示沒有子節點應當結束
        return
      }
      var parent = array[i]
      var bigChild = array[bigChildIndex]
      if (parent >= bigChild) {
        return
      }else{
        self.swapItemInArray(i,bigChildIndex)
        compareChildAndAdjust(bigChildIndex,lastIndex)//調整后要對子樹調整
      }
    }
    function findBigInChildren(i,lastIndex){
      var leftChild = array[2*i+1] //i節點的左子節點
      var rightChild = array[2*i+2] //i節點的右子節點
      if (lastIndex) {
        if (2*i+1 >= lastIndex) {
          return false
        }
        if (!(2*i+1 >= lastIndex) && (2*i+2 >= lastIndex)) {
          return 2*i+1
        }
      }
      if (!leftChild) {
        return false
      }
      if ( leftChild && !rightChild) {
        return 2*i+1
      }
      if (leftChild>rightChild) {
        return 2*i+1
      }else{
        return 2*i+2
      }
    }

堆排序的完整代碼如下

  this.heapSort = function(){
    var self = this
    createMaxHeap()
    swapMaxWithLast()
    function swapMaxWithLast(){
      var lastIndex = array.length - 1
      for (var i = lastIndex; i > 0; i--) {
        self.swapItemInArray(0,i)  //將根節點與最后一個節點交換
        //從根節點開始,與其子節點比較并重新形成最大堆
        //傳入的第二個參數表示,向下比較的時候,比到第i個節點之前停下來
        compareChildAndAdjust(0,i)  
      }
    }
    function createMaxHeap(){  //創建最大堆
      var len = array.length
      var startIndex = Math.floor(len/2) - 1 //從這個節點開始,將其子樹調整為最大堆

      for (var i = startIndex; i >= 0; i--) {
        compareChildAndAdjust(i)
      }
    }
    function compareChildAndAdjust(i,lastIndex){
      var bigChildIndex = findBigInChildren(i,lastIndex)
      if (bigChildIndex==false) {  //當找到的子節點返回為false時,表示沒有子節點應當結束
        return
      }
      var parent = array[i]
      var bigChild = array[bigChildIndex]
      if (parent >= bigChild) {
        return
      }else{
        self.swapItemInArray(i,bigChildIndex)
        compareChildAndAdjust(bigChildIndex,lastIndex)//調整后要對子樹調整
      }
    }
    function findBigInChildren(i,lastIndex){
      var leftChild = array[2*i+1] //i節點的左子節點
      var rightChild = array[2*i+2] //i節點的右子節點
      if (lastIndex) {
        if (2*i+1 >= lastIndex) {
          return false
        }
        if (!(2*i+1 >= lastIndex) && (2*i+2 >= lastIndex)) {
          return 2*i+1
        }
      }
      if (!leftChild) {
        return false
      }
      if ( leftChild && !rightChild) {
        return 2*i+1
      }
      if (leftChild>rightChild) {
        return 2*i+1
      }else{
        return 2*i+2
      }
    }
  }
各種排序的速度性能

首先用一個函數來隨機生成10萬個數

    function createNonSortedArray(size){
      var array = new ArrayList()
      for (var i = size; i > 0; i--) {
        let num = Math.floor(1 + Math.random()*99)
        array.insert(num)
      }
      return array
    }
    var arr = createNonSortedArray(100000)
    //console.log(arr.toString()) //打印查看生成結果

接下來采用如下函數來計算排序時間

var start = (new Date).getTime()
//在這里調用arr的各種排序方法
//如 arr.quickSort()
var end = (new Date).getTime()
console.log(end-start)  //打印查看生成結果
//console.log(arr.toString()) //打印查看排序結果

數據結果如下

冒泡排序耗時26000ms左右

選擇排序耗時5800ms左右

插入排序耗時10600ms左右

歸并排序耗時80-100ms

快速排序
cutoff==5--->30-50ms
cutoff==10 --->30-60ms
cutoff==50 ---->40-50ms
cutoff==3效果不錯--->30-50ms,30ms出現的機會很多
cutoff==0時(即不在分割長度短的時候轉為插入排序),效果依然不錯,30-50ms,30ms出現的很多

堆排序耗時120-140ms

JavaScript提供的原生排序耗時55-70ms

結論

快速排序效率最高,cutoff取3效果最好(沒有懸念)

原生排序竟然是第二快的排序算法!諸位同學參加筆試的時候,在沒有指明必須要用哪種排序算法的情況下,如果需要排個序,還是用原生的yourArr.sort(function(a,b){return a-b})吧,畢竟不易錯還特別快!

關于數據結構和排序算法的學習建議

如果想了解數據結構和排序算法的基礎理論知識,推薦中國大學mooc浙江大學陳越老師主講的《數據結構》。該課程采用C語言講解,但仍然可以系統地學習到數據結構的實現思路。
你要是覺得本文文字描述難以理解,去聽或看該課程的動態圖片講解應該會豁然開朗。

參考資料

《數據結構》(第2版),陳越,高等教育出版社
《學習JavaScript數據結構與算法》[巴西]Loiane Groner,中國工信出版集團,人民郵電出版社

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85069.html

相關文章

  • 2016年前端開發學習計劃

    摘要:年,軟件開發界發生了很多變化。六數據存儲是一個關系型數據庫管理系統,由瑞典公司開發,目前屬于旗下公司。最流行的關系型數據庫管理系統,在應用方面是最好的,關系數據庫管理系統應用軟件之一。七是最新的修訂版本,年月由萬維網聯盟完成標準制定。 2015年,軟件開發界發生了很多變化。有很多流行的新語言發布了,也有很多重要的框架和工具發布了新版本。下面有一個我們覺得最重要的簡短清單,同時也有我們覺...

    asoren 評論0 收藏0
  • 2016年前端開發學習計劃

    摘要:年,軟件開發界發生了很多變化。六數據存儲是一個關系型數據庫管理系統,由瑞典公司開發,目前屬于旗下公司。最流行的關系型數據庫管理系統,在應用方面是最好的,關系數據庫管理系統應用軟件之一。七是最新的修訂版本,年月由萬維網聯盟完成標準制定。 2015年,軟件開發界發生了很多變化。有很多流行的新語言發布了,也有很多重要的框架和工具發布了新版本。下面有一個我們覺得最重要的簡短清單,同時也有我們覺...

    Null 評論0 收藏0
  • 基于 Javascript 排序算法

    摘要:適用于數據比較少或基本有序的情況。插入排序時間復雜度為,空間復雜度為,屬于穩定排序。算法適用于少量數據的排序。就像下圖這樣,可以理解桶的意思下圖是整個排序過程示意圖基數排序時間復雜度為,空間復雜度為,屬于穩定排序。 寫在前面 個人感覺:javascript對類似排序查找這樣的功能已經有了很好的封裝,以致于當我們想對數組排序的時候只需要調用arr.sort()方法,而查找數組元素也只需要...

    tommego 評論0 收藏0
  • 常用排序算法JavaScript實現

    摘要:代碼實現六堆排序算法簡介堆排序是指利用堆這種數據結構所設計的一種排序算法。九計數排序算法簡介計數排序是一種穩定的排序算法。計數排序不是比較排序,排序的速度快于任何比較排序算法。 贊助我以寫出更好的文章,give me a cup of coffee? 2017最新最全前端面試題 1、插入排序 1)算法簡介 插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它...

    jerry 評論0 收藏0

發表評論

0條評論

yuanxin

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<