likes
comments
collection
share

图解轻松搞定你头疼的排序算法

作者站长头像
站长
· 阅读数 11

冒泡排序

依次比较相邻值,如果右侧较小交换位置,每次比较最右侧得到剩余的最大值0-i,相当于把最大值冒泡出去。

图解轻松搞定你头疼的排序算法

function bobolSort (arr) {
  const n = arr.length
  for (let i = 0; i < n; i++) {
    for (let j = 1; j < n - i; j++) {
       if (arr[j] < arr[j-1]) {
         swap(arr, j, j-1)
       }
    }
  }
}

function swap (arr, i, j) {
  arr[i] = arr[i] ^ arr[j]
  arr[j] = arr[i] ^ arr[j]
  arr[i] = arr[i] ^ arr[j]
}

选择排序

每次选出最小的,交换第一位和最小位,i-n-1

图解轻松搞定你头疼的排序算法

function selectSort (arr) {
    const n = arr.length
    for (let i = 0; i < n; i++) {
       let minIndex = i
       for (let j = i; j < n; j++) {
         minIndex = arr[j] < arr[minIndex] ? j : minIndex
       }
       swap(arr, i, minIndex)
    }
}

// 这里的交换函数不能用异或,因为i可能与j相等,会出现清零的错误
function swap (arr, i, j) {
   let temp = arr[i]
   arr[i] = arr[j]
   arr[j] = temp
}

插入排序

类似摸牌,依次搞定0-1,0-2...0-n-1的顺序

图解轻松搞定你头疼的排序算法

function insertSort (arr) {
   const n = arr.length
   for (let i = 1; i < n; i++) {
     for (let j = 0; j < i; j++) {
       if (arr[i] < arr[j]) {
          swap(arr, i, j)
       }
     }
   }
}

归并排序

递归,每次左侧排序,右侧排序,然后两个指针归并,相较上述3种,没有浪费比较行为

图解轻松搞定你头疼的排序算法

function mergeSort (arr) {
  if (arr.length <= 1) {
    return
  }
  process(arr, 0, arr.length - 1)
  console.log(arr)
}

function process (arr, l, r) {
  if (l === r) {
    return
  }
  let mid = l + ((r - l) >> 1)
  process(arr, l, mid);
  process(arr, mid+1, r); 
  merge(arr, l, mid, r)
}

function merge (arr, l, mid, r) {
  let newArr = []
  let p1 = l
  let p2 = mid + 1
  let i = 0
  while(p1 <= mid && p2 <= r) {
    newArr[i++] = arr[p1] > arr[p2] ? arr[p2++] : arr[p1++]
  }
  while(p1 <= mid) {
    newArr[i++] = arr[p1++]
  }
  while(p2 <= r) {
    newArr[i++] = arr[p2++]
  }
  for (let k = 0; k < newArr.length; k++) {
    // 这一步是必须的,只有改变原数组,才能在递归传递出去,使每次有效
    arr[l++] = newArr[k]
  }
}

举例:求小和问题

快速排序

先等概率随机选个数,小于这个数放左边和大于这个数放右边,然后递归,每次这个数的顺序会被排好 注意:快排选取的数必须是随机数,不能选指定某个位置的数,因为可能会出现最差情况的例子,时间复杂度就到了O(n²)了,只有选随机数,才会是O(nlogn)

图解轻松搞定你头疼的排序算法

function quickSort (arr) {
  if (arr.length <= 1) {
    return
  }
  process(arr, 0, arr.length - 1)
  console.log(arr)
}

function process (arr, l, r) {
  if (l < r) {
      // 选数
      let random = l + Math.floor(Math.random() * (r - l + 1))
      console.log('random', random)
      // 将选出来的随机数放在最右边
      swap(arr, random, r)
      let p = partition(arr, l, r)
      process(arr, l, p[0]-1)
      process(arr, p[1]+1, r)
  }

}

function partition (arr, l, r) {
  let p1 = l - 1 // 小于区右边界
  let p2 = r // 大于区左边界
  while (l < p2) {
    if (arr[l] > arr[r]) { // 当前值和划分值比较
      swap(arr, l, --p2)
    } else if (arr[l] < arr[r]) {
      swap(arr, ++p1, l++)
    } else {
      l++
    }
  }
  swap(arr, r, p2) // 交换最右的划分值和右边界-1
  return [p1+1, p2] // 返回等于划分值区域的左边界和右边界
}

function swap (arr, i, j) {
  let temp = arr[i]
  arr[i] = arr[j]
  arr[j] = temp
}

举例:荷兰国旗问题

堆排序

堆排序用得较少,后续更新哈

总结

排序稳定性:相同大小元素的相对次序,排序前后不变 图解轻松搞定你头疼的排序算法