JavaScript中的多種排序演算法

星夢花隨0823發表於2018-11-28

桶排序

  • 效能:浪費空間,且只能比較自然數,時間複雜度是 O(m+n)
  • 需要對資料範圍在0~n之間的整數進行排序
var testArr = [3, 5, 3, 5, 9, 7, 6]
console.log('原陣列:', arr)

function skipSort (n, skipArr) {
  let arr = []
  let sortArr = []
  arr.length = n
  for (let i = 0; i <= n; i++) {
    arr[i] = 0
  }
  for (let j = 0; j < skipArr.length; j++) {
    arr[skipArr[j]]++
  }
  // 這裡的判斷是決定 從小到大排序 或者是 從大到小排序 的關鍵
  // 如果要從大到小排序的話,條件語句是:for (t = n; t >= 0; t--)
  for (let t = 0; t <= n; t++) {
    if (arr[t]) {
      for (let k = 1; k <= arr[t]; k++) {
        sortArr.push(t)
      }
    }
  }
  return sortArr
}

console.log('桶排序--從小到大:', skipSort(10, testArr))
複製程式碼

氣泡排序

  • 效能:時間複雜度是O(N的2次方),執行效率低
  • 元素項向上移動至正確的順序,就好像氣泡往上冒一樣
  • 從小到大排序,比較兩個相鄰的項,如果第一個大於第二個則交換他們的位置
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原陣列:', arr)

function bubbleSort (arr) {
  let len = arr.length
  for (let i = 0; i < len; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
        // 這裡的判斷是決定 從小到大排序 或者是 從大到小排序 的關鍵
        // 如果要從大到小排序的話,條件語句是:if (arr[j] < arr[j + 1])
        if (arr[j] > arr[j + 1]) {
            let temp = arr[j]
            arr[j] = arr[j + 1]
            arr[j + 1] = temp
        }
    }
  }
  return arr
}
console.log('氣泡排序--從小到大:',bubbleSort(arr))
複製程式碼

快速排序

  • 效能:時間複雜度最差是O(N的2次方),平均時間複雜度為O(NlogN)
  • 首先,在陣列中選擇一箇中間項作為主元
  • 建立兩個指標,左邊的指向陣列第一個項,右邊的指向最後一個項。移動右指標,直到找到一個比主元小的項,接著,移動左邊的指標,直到找到一個比主元大的項,然後交換它們。重複這個過程,直到左側的指標超過了右側的指標。這個使比主元小的都在左側,比主元大的都在右側。這一步叫劃分操作
  • 接著,演算法對劃分後的小陣列(較主元小的值組成的的小陣列, 以及較主元大的值組成的小陣列)重複之前的兩個步驟,直到排序完成
  • 切記:一定要先從右往左找,再從左往右找,否則會出錯
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原陣列:', arr)

function quickSort (arr, left, right) {
  let len = arr.length
  left = typeof left === 'undefined' ? 0 : left
  right = typeof right === 'undefined' ? len - 1 : right
  if (left > right) {
    return
  }
  let temp = arr[left]
  let i = left
  let j = right
  let t
  while (i !== j) {
    // 此處 要先從右向左走,故先判斷右指標j
    
    // 這裡的判斷是決定 從小到大排序 或者是 從大到小排序 的關鍵
    // 如果要從大到小排序的話,條件語句是:while (arr[j] <= temp && i < j)
    while (arr[j] >= temp && i < j) {
      j--
    }
    // 這裡的判斷是決定 從小到大排序 或者是 從大到小排序 的關鍵
    // 如果要從大到小排序的話,條件語句是:while (arr[i] >= temp && i < j)
    while (arr[i] <= temp && i < j) {
      i++
    }
    if (i < j) {
      t = arr[i]
      arr[i] = arr[j]
      arr[j] = t
    }
  }
  arr[left] = arr[i]
  arr[i] = temp
  quickSort(arr, left, i - 1)
  quickSort(arr, i + 1, right)
  return arr
}
console.log('快速排序--從小到大:',quickSort(arr))
複製程式碼

選擇排序

  • 大概思路是找到最小的放在第一位,找到第二小的放在第二位,以此類推 演算法複雜度O(n^2)
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原陣列:', arr)

function selectSort (arr) {
  let len = arr.length
  let minIndex
  let temp
  for (let i = 0; i < len; i++) {
    minIndex = i
    temp = arr[i]
    for (let j = i + 1; j < len; j++) {
    // 這裡的判斷是決定 從小到大排序 或者是 從大到小排序 的關鍵
    // 如果要從大到小排序的話,條件語句是:if (arr[j] > arr[minIndex])
        if (arr[j] < arr[minIndex]) { 
            minIndex = j
        }
    }
    arr[i] = arr[minIndex]
    arr[minIndex] = temp
  }
  return arr
}
console.log('選擇排序--從小到大:',selectSort(arr))
複製程式碼

插入排序

  • 每次排一個陣列項,假設陣列的第一項已經排序
  • 接著,把第二項與第一項進行對比,第二項是該插入到第一項之前還是之後
  • 第三項是該插入到第一項之前還是第一項之後還是第三項
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原陣列:', arr)

function insertSort (arr) {
  let len = arr.length
  let preIndex
  for (let i = 1; i < len; i++) {
    preIndex = i - 1
    current = arr[i]
    // 這裡的判斷是決定 從小到大排序 或者是 從大到小排序 的關鍵
    // 如果要從大到小排序的話,條件語句是:while (preIndex >= 0 && arr[preIndex] < current)
    while (preIndex >= 0 && arr[preIndex] > current) {
      // arr[i] = arr[preIndex]
      arr[preIndex + 1] = arr[preIndex]
      preIndex--
    }
    // arr[i] = current
    arr[preIndex + 1] = current
  }
  return arr
}
console.log('插入排序--從小到大:', insertSort(arr))
複製程式碼

歸併排序

  • Mozilla Firefox使用歸併排序作為Array.prototype.sort的實現
  • chrome使用快速排序的一個變體實現Array.prototype.sort
  • 歸併排序是一種分治演算法。本質上就是把一個原始陣列切分成較小的陣列,直到每個小陣列只有一個位置,接著把小陣列歸併成較大的陣列,在歸併過程中也會完成排序,直到最後只有一個排序完畢的大陣列
  • 分治算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原陣列:', arr)

function mergeSort (arr) {
  let len = arr.length
  if (len < 2) {
    return arr
  }
  let middle = Math.floor(len / 2)
  let left = arr.slice(0, middle)
  let right = arr.slice(middle)
  return merge(mergeSort(left), mergeSort(right))
}

function merge (left, right) {
  let result = []
  while (left.length && right.length) {
    // 這裡的判斷是決定 從小到大排序 或者是 從大到小排序 的關鍵
    // 如果要從大到小排序的話,條件語句是:if (left[0] > right[0])
    if (left[0] < right[0]) {
      result.push(left.shift())
    } else {
      result.push(right.shift())
    }
  }
  result.push(...left)
  result.push(...right)
  return result
}
console.log('歸併排序--從小到大:', mergeSort(arr))
複製程式碼

各個排序演算法的效能比較

JavaScript中的多種排序演算法

相關文章