最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript快速排序算法不同版本原理解析

 更新時間:2023年08月15日 09:58:23   作者:刀法如飛  
這篇文章主要為大家介紹了JavaScript快速排序算法不同版本原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

說明

快速排序(QuickSort),又稱分區(qū)交換排序(partition-exchange sort),簡稱快排。快排是一種通過基準(zhǔn)劃分區(qū)塊,再不斷交換左右項的排序方式,其采用了分治法,減少了交換的次數(shù)。它的基本思想是:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分?jǐn)?shù)據(jù)分別進行快速排序,整個排序過程可以遞歸或迭代進行,以此讓整個數(shù)列變成有序序列。

實現(xiàn)過程

  • 在待排序區(qū)間找到一個基準(zhǔn)點(pivot),便于理解一般是位于數(shù)組中間的那一項。
  • 逐個循環(huán)數(shù)組將小于基準(zhǔn)的項放左側(cè),將大于基準(zhǔn)的項放在右側(cè)。一般通過交換的方式來實現(xiàn)。
  • 將基準(zhǔn)點左側(cè)全部項和基點右側(cè)全部項分別通過遞歸(或迭代)方式重復(fù)第1項,直到所有數(shù)組都交換完成。

示意圖

解釋:以某個數(shù)字為基點,這里取最右側(cè)的數(shù)字8,以基點劃分為兩個區(qū)間,將小于8的數(shù)字放在左側(cè)區(qū)間,將大于8的數(shù)字放在右側(cè)區(qū)間。再將左側(cè)區(qū)間和右側(cè)區(qū)間分別放到遞歸,按照最右側(cè)為基點,繼續(xù)分解。直到分解完畢,排序完成。這是其中一種常見的分區(qū)遞歸法,除了這種方式外,還有其他實現(xiàn)方式。

性能分析

平均時間復(fù)雜度:O(NlogN)

最佳時間復(fù)雜度:O(NlogN)

最差時間復(fù)雜度:O(N^2)

空間復(fù)雜度:根據(jù)實現(xiàn)方式的不同而不同,可以查看不同版本的源碼

快排方式1, 新建數(shù)組遞歸版本

無需交換,每個分區(qū)都是新數(shù)組,數(shù)量龐大。

這個版本利用了JS數(shù)組可變且隨意拼接的特性,讓每個分區(qū)都是一個新數(shù)組,從而無需交換數(shù)組項。
這個方式非常簡單易懂,但理論上來講不是完全意義上的快排,效率較差。

function quickSort1(arr) {
  // 數(shù)組長度為1就不再分解
  console.log('origin array:', arr)
  if (arr.length <= 1) {
    return arr
  }
  var pivot
  const left = []
  const right = []
  // 設(shè)置中間數(shù),取最中間的項
  var midIndex = Math.floor(arr.length / 2)
  pivot = arr[midIndex]
  for (var i = 0, l = arr.length; i < l; i++) {
    console.log('i=' + i + ' midIndex=' + midIndex + ' pivot=' + pivot + ' arr[]=' + arr)
    // 當(dāng)中間基數(shù)等于i時跳過?;鶖?shù)數(shù)待遞歸完成時合并到到新數(shù)組。
    if (midIndex === i) {
      continue
    }
    // 當(dāng)前數(shù)組里面的項小于基數(shù)則添加到左側(cè)
    if (arr[i] < pivot) {
      left.push(arr[i])
      // 大于等于則添加到右側(cè)
    } else {
      right.push(arr[i])
    }
  }
  arr = quickSort1(left).concat(pivot, quickSort1(right))
  console.log('sorted array:', arr)
  // 遞歸調(diào)用遍歷左側(cè)和右側(cè),再將中間值連接起來
  return arr
}

遞歸的過程

// 基于中間數(shù)進行遞歸分解:
      f([7, 11, 9, 10, 12, 13, 8])
            /       10          \
      f([7, 9, 8])           f([11, 12, 13])
        /   9    \             /    12     \
   f([7, 8])    f([])       f([11])       f[13]
   /   8  \
f([7]) f([])
  [7]
// 將遞歸后的最小單元和基數(shù)連接起來
// 得到:[7, 8, 9, 10, 11, 12, 13]

快排方式2, 標(biāo)準(zhǔn)分區(qū)遞歸版本

左右分區(qū)遞歸交換排序,無需新建數(shù)組。

這個版本是最常見的標(biāo)準(zhǔn)分區(qū)版本,簡單好懂。先寫一個分區(qū)函數(shù),依據(jù)基準(zhǔn)值把成員項分為左右兩部分?;鶞?zhǔn)值可以是數(shù)列中的任意一項,為了交換方便,基準(zhǔn)值一般最左或最右側(cè)項。把小于基準(zhǔn)值的放在左側(cè),大于基準(zhǔn)值的放在右側(cè),最后返回分區(qū)索引。這樣就得到一個基于基準(zhǔn)值的左右兩個部分。再將左右兩個部分,分別進行分區(qū)邏輯的遞歸調(diào)用,當(dāng)左右值相等,也就是最小分區(qū)只有1項時終止。

// 分區(qū)函數(shù),負(fù)責(zé)把數(shù)組分按照基準(zhǔn)值分為左右兩部分
// 小于基準(zhǔn)的在左側(cè),大于基準(zhǔn)的在右側(cè)最后返回基準(zhǔn)值的新下標(biāo)
function partition(arr, left, right) {
  // 基準(zhǔn)值可以是left與right之間的任意值,再將基準(zhǔn)值移動至最左或最右即可。
  // 直接基于中間位置排序,則需要基于中間位置左右交換,參加基于中間位置交換的版本。
  // var tmpIndex = Math.floor((right - left) / 2)
  // ;[arr[left + tmpIndex], arr[right]] = [arr[right], arr[left + tmpIndex]]
  var pivotIndex = right
  var pivot = arr[pivotIndex]
  var partitionIndex = left - 1
  for (var i = left; i < right; i++) {
    // 如果比較項小于基準(zhǔn)值則進行交換,并且分區(qū)索引增加1位
    // 也就是將大于基準(zhǔn)值的全部往右側(cè)放,以分區(qū)索引為分割線
    if (arr[i] < pivot) {
      partitionIndex++
      if (partitionIndex !== i) {
        [arr[partitionIndex], arr[i]] = [arr[i], arr[partitionIndex]]
      }
    }
  }
  partitionIndex++;
  [arr[partitionIndex], arr[pivotIndex]] = [arr[pivotIndex], arr[partitionIndex]]
  return partitionIndex
}
// 分區(qū)遞歸版本,分區(qū)遞歸調(diào)用。
function quickSort2(arr, left, right) {
  left = left !== undefined ? left : 0
  right = right !== undefined ? right : arr.length - 1
  if (left < right) {
    var pivot = partition(arr, left, right)
    quickSort2(arr, left, pivot - 1)
    quickSort2(arr, pivot + 1, right)
  }
  return arr
}

快排方式3, 標(biāo)準(zhǔn)左右交換遞歸版本

基于中間位置不斷左右交換,無需新建數(shù)組。

此版本基于中間位置,建立雙指針,同時從前往后和從后往前遍歷,從左側(cè)找到大于基準(zhǔn)值的項,從右側(cè)找到小于基準(zhǔn)值的項。

再將大于基準(zhǔn)值的挪到右側(cè),將小于基準(zhǔn)值的項挪到左側(cè),直到左側(cè)位置大于右側(cè)時終止。左側(cè)位置小于基準(zhǔn)位置則遞歸調(diào)用左側(cè)區(qū)間,右側(cè)大于基準(zhǔn)位置則遞歸調(diào)用右側(cè)區(qū)間,直到所有項排列完成。

function quickSort3(arr, left, right) {
  var i = left = left !== undefined ? left : 0
  var j = right = right !== undefined ? right : arr.length - 1
  // 確定中間位置,基于中間位置不停左右交換
  var midIndex = Math.floor((i + j) / 2)
  var pivot = arr[midIndex]
  // 當(dāng)左側(cè)小于等于右側(cè)則表示還有項沒有對比,需要繼續(xù)
  while (i <= j) {
    // 當(dāng)左側(cè)小于基準(zhǔn)時查找位置右移,直到找出比基準(zhǔn)值大的位置來
    while (arr[i] < pivot) {
      console.log('arr[i] < pivot:', ' i=' + i + ' j=' + j + ' pivot=' + pivot)
      i++
    }
    // 當(dāng)前右側(cè)大于基準(zhǔn)時左移,直到找出比基準(zhǔn)值小的位置來
    while (arr[j] > pivot) {
      console.log('arr[j] > pivot:', ' i=' + i + ' j=' + j + ' pivot=' + pivot)
      j--
    }
    console.log('  left=' + left + ' right=' + right + ' i=' + i + ' j=' + j + ' midIndex=' + midIndex + ' pivot=' + pivot + ' arr[]=' + arr)
    // 當(dāng)左側(cè)位置小于等于右側(cè)時,將數(shù)據(jù)交換,小的交換到基準(zhǔn)左側(cè),大的交換到右側(cè)
    if (i <= j) {
      [arr[i], arr[j]] = [arr[j], arr[i]]
      // 縮小搜查范圍,直到左側(cè)都小于基數(shù),右側(cè)都大于基數(shù)
      i++
      j--
    }
  }
  // 左側(cè)小于基數(shù)位置,不斷遞歸左邊部分
  if (left < j) {
    console.log('left < j:recursion:  left=' + left + ' right=' + right + ' i=' + i + ' j=' + j + 'arr[]' + arr)
    quickSort3(arr, left, j)
  }
  // 基數(shù)位置小于右側(cè),不斷遞歸右側(cè)部分
  if (i < right) {
    console.log('i < right:recursion:  left=' + left + ' right=' + right + ' i=' + i + ' j=' + j + 'arr[]' + arr)
    quickSort3(arr, i, right)
  }
  return arr
}

快排方式4, 非遞歸左右交換版本

基于中間位置不斷左右交換,利用stack或queue遍歷。

這種方式標(biāo)準(zhǔn)左右交換遞歸版本的非遞歸版本,其原理一樣,只是不再遞歸調(diào)用,而是通過stack來模擬遞歸效果。這種方式性能最好。

function quickSort4(arr, left, right) {
  left = left !== undefined ? left : 0
  right = right !== undefined ? right : arr.length - 1
  var stack = []
  var i, j, midIndex, pivot, tmp
  // 與標(biāo)準(zhǔn)遞歸版相同,只是將遞歸改為遍歷棧的方式
  // 先將左右各取一個入棧
  stack.push(left)
  stack.push(right)
  while (stack.length) {
    // 如果棧內(nèi)還有數(shù)據(jù),則一并馬上取出,其他邏輯與標(biāo)準(zhǔn)遞歸版同
    j = right = stack.pop()
    i = left = stack.pop()
    midIndex = Math.floor((i + j) / 2)
    pivot = arr[midIndex]
    while (i <= j) {
      while (arr[i] < pivot) {
        console.log('arr[i] < pivot:', ' i=' + i + ' j=' + j + ' pivot=' + pivot + 'arr[]=' + arr)
        i++
      }
      while (arr[j] > pivot) {
        console.log('arr[j] > pivot:', ' i=' + i + ' j=' + j + ' pivot=' + pivot + 'arr[]=' + arr)
        j--
      }
      if (i <= j) {
        tmp = arr[j]
        arr[j] = arr[i]
        arr[i] = tmp
        i++
        j--
      }
    }
    if (left < j) {
      // 與遞歸版不同,這里當(dāng)左側(cè)小于基數(shù)位置時添加到棧中,以便繼續(xù)循環(huán)
      console.log('left < j:recursion:  left=' + left + ' right=' + right + ' i=' + i + ' j=' + j + 'arr[]=' + arr)
      stack.push(left)
      stack.push(j)
    }
    if (i < right) {
      // 當(dāng)右側(cè)大于等于基數(shù)位置時添加到棧中,以便繼續(xù)循環(huán)
      console.log('i < right:recursion:  left=' + left + ' right=' + right + ' i=' + i + ' j=' + j + 'arr[]=' + arr)
      stack.push(i)
      stack.push(right)
    }
  }
  return arr
}

測試

(function () {
  const arr = [7, 11, 9, 10, 12, 13, 8]
  // 構(gòu)建數(shù)列,可以任意構(gòu)建,支持負(fù)數(shù),也不限浮點
  // const arr = [17, 31, 12334, 9.545, -10, -12, 1113, 38]
  console.time('sort1')
  const arr1 = arr.slice(0)
  console.log('sort1 origin:', arr1)
  console.log('\r\nquickSort1 sorted:', quickSort1(arr1))
  console.timeEnd('sort1')
  console.time('sort2')
  const arr2 = arr.slice(0)
  console.log('sort2 origin:', arr2)
  console.log('\r\nquickSort2 sorted:', quickSort2(arr2))
  console.timeEnd('sort2')
  console.time('sort3')
  const arr3 = arr.slice(0)
  console.log('sort3 origin:', arr3)
  console.log('\r\nquickSort3 sorted:', quickSort3(arr3))
  console.timeEnd('sort3')
  console.time('sort4')
  const arr4 = arr.slice(0)
  console.log('sort4 origin:', arr4)
  console.log('\r\nquickSort4 sorted:', quickSort4(arr4))
  console.timeEnd('sort4')
})()
/**
// 測試結(jié)果
jarry@jarrys-MacBook-Pro quicksort % node quick_sort.js
sort1 origin: [
   7, 11, 9, 10,
  12, 13, 8
]
origin array: [
   7, 11, 9, 10,
  12, 13, 8
]
i=0 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
i=1 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
i=2 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
i=3 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
i=4 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
i=5 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
i=6 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
origin array: [ 7, 9, 8 ]
i=0 midIndex=1 pivot=9 arr[]=7,9,8
i=1 midIndex=1 pivot=9 arr[]=7,9,8
i=2 midIndex=1 pivot=9 arr[]=7,9,8
origin array: [ 7, 8 ]
i=0 midIndex=1 pivot=8 arr[]=7,8
i=1 midIndex=1 pivot=8 arr[]=7,8
origin array: [ 7 ]
origin array: []
sorted array: [ 7, 8 ]
origin array: []
sorted array: [ 7, 8, 9 ]
origin array: [ 11, 12, 13 ]
i=0 midIndex=1 pivot=12 arr[]=11,12,13
i=1 midIndex=1 pivot=12 arr[]=11,12,13
i=2 midIndex=1 pivot=12 arr[]=11,12,13
origin array: [ 11 ]
origin array: [ 13 ]
sorted array: [ 11, 12, 13 ]
sorted array: [
   7,  8,  9, 10,
  11, 12, 13
]
quickSort1 sorted: [
   7,  8,  9, 10,
  11, 12, 13
]
sort1: 9.824ms
sort2 origin: [
   7, 11, 9, 10,
  12, 13, 8
]
partitioned arr= [
   7,  8,  9, 10,
  12, 13, 11
] partitionIndex: 1 left= [ 7 ] arr[partitionIndex]= 8 right= [ 8, 9, 10, 12, 13, 11 ] [
   7,  8,  9, 10,
  12, 13, 11
]
partitioned arr= [
   7,  8,  9, 10,
  11, 13, 12
] partitionIndex: 4 left= [ 9, 10 ] arr[partitionIndex]= 11 right= [ 11, 13, 12 ] [
   7,  8,  9, 10,
  11, 13, 12
]
partitioned arr= [
   7,  8,  9, 10,
  11, 13, 12
] partitionIndex: 3 left= [ 9 ] arr[partitionIndex]= 10 right= [ 10 ] [
   7,  8,  9, 10,
  11, 13, 12
]
partitioned arr= [
   7,  8,  9, 10,
  11, 12, 13
] partitionIndex: 5 left= [] arr[partitionIndex]= 12 right= [ 12, 13 ] [
   7,  8,  9, 10,
  11, 12, 13
]
quickSort2 sorted: [
   7,  8,  9, 10,
  11, 12, 13
]
sort2: 1.15ms
sort3 origin: [
   7, 11, 9, 10,
  12, 13, 8
]
arr[i] < pivot:  i=0 j=6 pivot=10
  left=0 right=6 i=1 j=6 midIndex=3 pivot=10 arr[]=7,11,9,10,12,13,8
arr[i] < pivot:  i=2 j=5 pivot=10
arr[j] > pivot:  i=3 j=5 pivot=10
arr[j] > pivot:  i=3 j=4 pivot=10
  left=0 right=6 i=3 j=3 midIndex=3 pivot=10 arr[]=7,8,9,10,12,13,11
left < j:recursion:  left=0 right=6 i=4 j=2arr[]7,8,9,10,12,13,11
arr[i] < pivot:  i=0 j=2 pivot=8
arr[j] > pivot:  i=1 j=2 pivot=8
  left=0 right=2 i=1 j=1 midIndex=1 pivot=8 arr[]=7,8,9,10,12,13,11
i < right:recursion:  left=0 right=6 i=4 j=2arr[]7,8,9,10,12,13,11
arr[i] < pivot:  i=4 j=6 pivot=13
  left=4 right=6 i=5 j=6 midIndex=5 pivot=13 arr[]=7,8,9,10,12,13,11
left < j:recursion:  left=4 right=6 i=6 j=5arr[]7,8,9,10,12,11,13
  left=4 right=5 i=4 j=5 midIndex=4 pivot=12 arr[]=7,8,9,10,12,11,13
quickSort3 sorted: [
   7,  8,  9, 10,
  11, 12, 13
]
sort3: 0.595ms
sort4 origin: [
   7, 11, 9, 10,
  12, 13, 8
]
arr[i] < pivot:  i=0 j=6 pivot=10arr[]=7,11,9,10,12,13,8
arr[i] < pivot:  i=2 j=5 pivot=10arr[]=7,8,9,10,12,13,11
arr[j] > pivot:  i=3 j=5 pivot=10arr[]=7,8,9,10,12,13,11
arr[j] > pivot:  i=3 j=4 pivot=10arr[]=7,8,9,10,12,13,11
left < j:recursion:  left=0 right=6 i=4 j=2arr[]=7,8,9,10,12,13,11
i < right:recursion:  left=0 right=6 i=4 j=2arr[]=7,8,9,10,12,13,11
arr[i] < pivot:  i=4 j=6 pivot=13arr[]=7,8,9,10,12,13,11
left < j:recursion:  left=4 right=6 i=6 j=5arr[]=7,8,9,10,12,11,13
arr[i] < pivot:  i=0 j=2 pivot=8arr[]=7,8,9,10,11,12,13
arr[j] > pivot:  i=1 j=2 pivot=8arr[]=7,8,9,10,11,12,13
quickSort4 sorted: [
   7,  8,  9, 10,
  11, 12, 13
]
sort4: 0.377ms
 */

鏈接

多種語言實現(xiàn)快速排序算法源碼:https://github.com/microwind/algorithms/tree/master/sorts/quicksort

其他排序算法源碼:https://github.com/microwind/algorithms

以上就是JavaScript快速排序算法不同版本原理解析的詳細(xì)內(nèi)容,更多關(guān)于JavaScript快速排序算法的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript TypeScript實現(xiàn)貪吃蛇游戲完整詳細(xì)流程

    JavaScript TypeScript實現(xiàn)貪吃蛇游戲完整詳細(xì)流程

    這篇文章主要介紹了JavaScript TypeScript實現(xiàn)貪吃蛇游戲流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09
  • 微信小程序?qū)崿F(xiàn)的自定義分享功能示例

    微信小程序?qū)崿F(xiàn)的自定義分享功能示例

    這篇文章主要介紹了微信小程序?qū)崿F(xiàn)的自定義分享功能,結(jié)合實例形式分析了微信小程序自定義分享功能的原理與簡單實現(xiàn)技巧,需要的朋友可以參考下
    2019-02-02
  • JS中的二叉樹遍歷詳解

    JS中的二叉樹遍歷詳解

    這篇文章主要為大家詳細(xì)介紹了JS中的二叉樹遍歷,何為二叉樹,什么是二叉樹的遍歷,感興趣的小伙伴們可以參考一下
    2016-03-03
  • 原生js實現(xiàn)倒計時--2018

    原生js實現(xiàn)倒計時--2018

    本文主要介紹了原生js實現(xiàn)倒計時--2018的示例代碼,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • ES6中解構(gòu)賦值實現(xiàn)變量批量賦值解放雙手

    ES6中解構(gòu)賦值實現(xiàn)變量批量賦值解放雙手

    這篇文章主要為大家介紹了ES6中解構(gòu)賦值實現(xiàn)變量批量賦值解放雙手,變量的解構(gòu)賦值,聽起來很復(fù)雜,簡單點說可以理解成批量操作變量賦值
    2022-04-04
  • javascript中的數(shù)據(jù)類型檢測方法詳解

    javascript中的數(shù)據(jù)類型檢測方法詳解

    這篇文章主要介紹了javascript中的數(shù)據(jù)類型檢測方法,結(jié)合實例形式分析了javascript數(shù)據(jù)類型并總結(jié)分析了常見的數(shù)據(jù)類型檢測操作技巧,需要的朋友可以參考下
    2019-08-08
  • button沒寫type=button會導(dǎo)致點擊時提交

    button沒寫type=button會導(dǎo)致點擊時提交

    點擊了一個彈窗中的按鈕,想到彈窗消失了,經(jīng)測試后發(fā)現(xiàn)button 沒寫type=button會導(dǎo)致點擊時提交
    2014-03-03
  • JS setCapture 區(qū)域外事件捕捉

    JS setCapture 區(qū)域外事件捕捉

    鼠標(biāo)捕獲(setCapture)作用是將鼠標(biāo)事件捕獲到當(dāng)前文檔的指定的對象。這個對象會為當(dāng)前應(yīng)用程序或整個系統(tǒng)接收所有鼠標(biāo)事件。
    2010-03-03
  • 自己使用js/jquery寫的一個定制對話框控件

    自己使用js/jquery寫的一個定制對話框控件

    自己做一個通用的控件,雖然不是絕對通用啦,但在我這個項目里還是可以隨意調(diào)用的,思想的話也可以借鑒到別的項目中
    2014-05-05
  • JS實現(xiàn)延時執(zhí)行幾種方法總結(jié)

    JS實現(xiàn)延時執(zhí)行幾種方法總結(jié)

    JavaScript延時器是實現(xiàn)異步操作和延遲執(zhí)行代碼的重要工具,這篇文章主要介紹了JS實現(xiàn)延時執(zhí)行幾種方法的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-11-11

最新評論

宣威市| 阿坝| 安吉县| 武定县| 通海县| 明溪县| 鄂尔多斯市| 峨边| 玛曲县| 无为县| 旬阳县| 北京市| 慈溪市| 海丰县| 三江| 大兴区| 云梦县| 泗洪县| 凌海市| 裕民县| 开化县| 色达县| 石城县| 玉屏| 黎城县| 郧西县| 博客| 绥宁县| 台中市| 乳山市| 滦南县| 湘乡市| 英山县| 宿迁市| 临洮县| 英吉沙县| 建平县| 五峰| 安吉县| 肇州县| 双鸭山市|