經常使用的四種排序算法

背景: 前段時間, 某前端校招二面上來就讓寫快排, 當時就懵了, 想着好久之前看過的, 忘了, 血虧. 最後是掛了, 可能第一印象就不行. 如今記錄一下, 也避免你們遇到這種狀況javascript

插入排序

思想

二分查找並插入元素

  1. 對於有序數組, 插入一個值使得數組仍然有序
  2. 選擇有序數組中間元素, 將有序數組分紅先後兩部分
  3. 比較待插入元素與中間元素
  4. 待插入元素大於中間元素則插入後半部分, 不然插入前半部分
  5. 插入前/後半部分的方式重複步驟2, 3, 4

排序

  1. 有一個待排序數組, 和一個做爲容器的空數組
  2. 從待排序數組中選擇一個元素插入到容器數組, 並保持其有序

代碼

const numbers = [2, 4, 6, 12, 43, 12, 56, 1]

function insertionSort(arr) {
  function binInsert(val, arr) {
    if (arr.length === 0) {
      return [val]
    } else if (arr.length === 1) {
      return val > arr[0] ? [arr[0], val] : [val, arr[0]]
    } else {
      const middle = Math.floor(arr.length / 2)
      return val > arr[middle] ?
        [...arr.slice(0, middle), ...binInsert(val, arr.slice(middle))] :
        [...binInsert(val, arr.slice(0, middle)), ...arr.slice(middle)]
    }
  }
  return arr.reduce((buf, val) => binInsert(val, buf), [])
}

console.log(insertionSort(numbers))
複製代碼

歸併排序

思想

先分解, 後歸併前端

歸併

  1. 有兩個有序數組(從小到大), 和一個做爲容器的空數組
  2. 各從兩數組頭部彈出一個元素
  3. 比較兩個彈出元素大小, 較小的元素從容器尾部推入, 較大元素放回原數組
  4. 重複步驟2, 3, 直至兩個有序數組之一爲空
  5. 當兩個有序數組之一爲空時, 將非空數組的頭部與容器數組的尾部鏈接成一個新數組
  6. 新的數組即爲排序好的數組

分解

  1. 將待排序數組分爲兩個數組
  2. 將分出來的兩個數組再各自分紅兩個數組
  3. 重複步驟2, 直至分紅的數組只有兩個元素或更少
  4. 對於只有兩個元素的數組, 經過比較便可排序
  5. 歸併各組分解出來的數組

代碼

const numbers = [2, 4, 6, 12, 43, 12, 56, 1]

function mergeSort(arr) {
  if (arr.length < 2) {
    return arr
  } else if (arr.length === 2) {
    return arr[0] > arr[1] ?
      [arr[1], arr[0]] :
      [arr[0], arr[1]]
  }
  else {
    const buf = []
    const middle = Math.floor(arr.length / 2)
    const part0 = mergeSort(arr.slice(0, middle))
    const part1 = mergeSort(arr.slice(middle))
    for (let i = 0, j = 0; i < part0.length || j < part1.length;) {
      const val0 = part0[i]
      const val1 = part1[j]
      if (val0 && val1) {
        if (val0 < val1) {
          buf.push(val0)
          i++
        } else {
          buf.push(val1)
          j++
        }
      } else if (val0) {
        buf.push(val0)
        i++
      } else if (val1) {
        buf.push(val1)
        j++
      }
    }
    return buf
  }
}

console.log(mergeSort(numbers))
複製代碼

冒泡排序

思想

  1. 有一個待排序數組, 一個空的數組做爲容器
  2. 對於待排序數組, 從頭到尾, 兩兩比較元素, 交換位置(小的在前)
  3. 步驟2使得待排序數組中最大元素位於最後
  4. 最大元素從容器數組頭部推入
  5. 最大元素以前的全部元素構成一個新的待排序數組
  6. 重複步驟2, 3, 4, 5, 直至新的待排序數組只含一個元素
  7. 將這個新的待排序數組的惟一元素從容器數組頭部推入

代碼

const numbers = [2, 4, 6, 12, 43, 12, 56, 1]

function bubbleSort(arr) {
  if (arr.length < 2) {
    return arr
  } else {
    for (let i = 1; i < arr.length; i++) {
      if (arr[i] < arr[i - 1])
        [arr[i - 1], arr[i]] = [arr[i], arr[i - 1]]
    }
    return [...bubbleSort(arr.slice(0, -1)), ...arr.slice(-1)]
  }
}

console.log(bubbleSort(numbers))
複製代碼

快速排序

思想

  1. 有一個待排序數組
  2. 從待排序數組中選擇一個元素做爲基準值
  3. 遍歷待排序數組, 小於基準值的元素構成一個數組(less), 大於基準值的元素也構成一個數組(greater)
  4. 若是lessgreater都已經排序好了, 鏈接less, 基準值, greater即獲得最終排序好的數組
  5. 對待排序數組分解出來的lessgreater分別應用步驟2, 3, 4

代碼

const numbers = [2, 4, 6, 12, 43, 12, 56, 1]

function quickSort(arr) {
  if (arr.length < 2) {
    return arr
  } else {
    const pivot = arr[0], less = [], greater = []
    for (let i = 1; i < arr.length; i++) {
      const num = arr[i];
      num > pivot ? greater.push(num) : less.push(num)
    }
    return [...quickSort(less), pivot, ...quickSort(greater)]
  }
}

console.log(quickSort(numbers))
複製代碼

睡眠排序

四大xxxx有五個顯然是常識java

思想

  1. 有一個待排序數組, 一個空的數組做爲容器
  2. 對於待排序數組每一個元素, 使用元素的值做爲時間, 設置一個相應的定時器
  3. 定時器歸零時, 將對應元素從容器數組尾部推入

代碼

const numbers = [2, 4, 6, 12, 43, 12, 56, 1]

async function sleepSort(arr) {
  const sorted = []
  await Promise.all(numbers.map(t => new Promise((res) => {
    const timer = setTimeout(() => {
      clearTimeout(timer)
      sorted.push(t)
      res()
    }, t)
  })))
  return sorted
}

sleepSort(numbers).then(sorted => console.log(sorted))
複製代碼
相關文章
相關標籤/搜索