使用javascript实现主要排序算法。
// 测试用例
const list = [2, 5, 4, 1, 9, 7, 10, 8, 3, 6]
1-排序操作
排序涉及比较、交换、拷贝等操作。
// utils/index.js
export default class Comparator {
/**
* @param {function(a: *,b: *)} [compareFunction]
*/
constructor(compareFunction) {
this.compare = compareFunction || Comparator.defaultCompareFunction
}
/**
* @param {(string|number)} a
* @param {(string|number)} b
* @returns {number}
*/
static defaultCompareFunction(a, b) {
if (a === b) return 0
return a < b ? -1 : 1
}
equal(a, b) {
return this.compare(a, b) === 0
}
lessThan(a, b) {
return this.compare(a, b) < 0
}
greaterThan(a, b) {
return this.compare(a, b) > 0
}
lessThanOrEqual(a, b) {
return this.lessThan(a, b) || this.equal(a, b)
}
greaterThanOrEqual(a, b) {
return this.greaterThan(a, b) || this.equal(a, b)
}
}
export function swap(fromIndex, toIndex, list) {
const temp = list[fromIndex]
list[fromIndex] = list[toIndex]
list[toIndex] = temp
}
const comparator = new Comparator()
export const pairsInOrder = (firstElement, secondElement, ascending = true) => {
const lessThan = comparator.lessThan(firstElement, secondElement)
const greaterThan = comparator.greaterThan(firstElement, secondElement)
return ascending ? lessThan : greaterThan
}
export function copy(destination, source, length, sStart = 0 , dStart = 0) {
if (sStart + length > source.length) {
length = souce.length - sStart
}
length = Math.min(destination.length - dStart, length)
while (dStart < length) {
destination[dStart] = source[sStart]
dStart++
sStart++
}
}
2-选择排序
常见的选择排序:简单选择排序和堆排序。
1-1 简单选择排序
循环遍历列表,依次选择列表中中的最小值进行增序排列, 或最大值进行降序
// sort/select/selection.js
import { pairsInOrder } from "./utils"
export default function selectSort(list, ascending = true) {
for (let i = 0; i < list.length - 1; i++) {
let temp = list[i]
for (let j = i + 1; j < list.length; j++) {
if (pairsInOrder(list[j], temp, ascending)) {
temp = list[j]
list[j] = list[i]
list[i] = temp
}
}
}
return list
}
1-2 堆排序
构造最大堆进行升序排列,或最小堆进行降序排列。
// sort/select/heap.js
export default class Heap {
/**
* @param {function(a: *,b: *)} [compareFunction]
* @param {boolean} ascending --- true 升序,最大堆; false 降序, 最小堆
* @returns {number}
*/
constructor(compareFunction, ascending = true) {
this.heap = []
this.ascending = ascending this.lastIndex = -1
this.comparator = new Comparator(compareFunction)
}
getParentIndex(childIndex) {
return Math.floor((childIndex - 1) / 2)
}
getLeftChildIndex(parentIndex) {
return 2 * parentIndex + 1
}
getRightChildIndex(parentIndex) {
return this.getLeftChildIndex(parentIndex) + 1
}
append(node) {
this.heap[++this.lastIndex] = node
this.trickleUp(this.lastIndex)
}
swap(fromIndex, toIndex) {
const temp = this.heap[fromIndex]
this.heap[fromIndex] = this.heap[toIndex]
this.heap[toIndex] = temp
}
remove() {
if(this.lastIndex === -1) return null
const temp = this.heap[0]
this.swap(0, this.lastIndex--)
this.trickleDown()
return temp
}
trickleUp(childIndex) {
if(childIndex === 0) return
const parentIndex = this.getParentIndex(childIndex)
if(this.pairsInOrder(this.heap[parentIndex], this.heap[childIndex])) {
this.swap(parentIndex, childIndex)
this.trickleUp(parentIndex)
}
}
trickleDown(parentIndex = 0) {
if (this.lastIndex === -1) return
const leftChildIndex = this.getLeftChildIndex(parentIndex)
const rightChildIndex = this.getRightChildIndex(parentIndex)
if (leftChildIndex === this.lastIndex && this.pairsInOrder(this.heap[parentIndex], this.heap[leftChildIndex])) {
this.swap(parentIndex, leftChildIndex)
return
}
if (this.comparator.lessThanOrEqual(this.lastIndex, leftChildIndex) || this.comparator.lessThan(this.lastIndex, rightChildIndex)) return
if (this.pairsInOrder(this.heap[rightChildIndex], this.heap[leftChildIndex]) && this.pairsInOrder(this.heap[parentIndex], this.heap[leftChildIndex])) {
this.swap(parentIndex, leftChildIndex)
this.trickleDown(leftChildIndex)
}
if (this.pairsInOrder(this.heap[leftChildIndex], this.heap[rightChildIndex]) && this.pairsInOrder(this.heap[parentIndex], this.heap[rightChildIndex])) {
this.swap(parentIndex, rightChildIndex)
this.trickleDown(rightIndex)
}
}
pairsInOrder(firstElement, secondElement) {
const lessThan = this.comparator.lessThan(firstElement, secondElement)
const greaterThan = this.comparator.greaterThan (firstElement, secondElement)
return this.ascending ? lessThan : greaterThan
}
}
3-交换排序
常见的交换排序:冒泡排序和快速排序。
3-1 冒泡排序
// sort/exchange/bubble.js
import { pairsInOrder, swap } from "./utils"
export function bubbleSort(list, ascending) {
for (let i = 1; i < list.length; i++) {
for (let j = 0; j < list.length - i; j++) {
if (pairsInOrder(list[j+1], list[j], ascending)) {
swap(j, j + 1, list)
}
}
}
return list
}
export default function improvedBubbleSort(list, ascending) {
let exchange = list.length
while (exchange) {
const bound = exchange
exchange = 0
for (let i = 0; i < bound; i++) {
if (pairsInOrder(list[i + 1], list[i], ascending)) {
swap(i, i + 1, list)
exchange = i
}
}
}
return list
}
3-2 快速排序
// sort/excahnge/quick.js
const { swap, pairsInOrder } = require("./compare")
export function quickSort(list, ascending) {
const quickPass = (from, to) => {
if (to <= from) return
let i = j = from
while (i < to) {
if (pairsInOrder(list[i], list[to], ascending)) {
swap(i, j, list)
j++
}
i++
}
swap(j, to, list)
quickPass(from, j - 1)
quickPass(j + 1, to)
}
quickPass(0, list.length - 1)
return list
}
4-插入排序
4-1 简单插入
// sort/insert/simple.js
const { swap, pairsInOrder } = require("./compare")
export function insertSort(list, ascending) {
for (let i = 1; i < list.length; i++) {
let temp = list[i]
let j = i - 1;
while (j >= 0) {
if (!pairsInOrder(temp, list[j], ascending)) break
swap(j, j + 1, list)
j--
}
list[j + 1] = temp
}
return list
}
4-2 希尔排序
// sort/insert/shell.js
const { swap, pairsInOrder } = require("./compare")
export default function shellSort(list, steps, ascending) {
const shellPass = (step) => {
for (let i = step; i < list.length; i++) {
let temp = list[i]
let j = i - step;
while (j >= 0) {
if (!pairsInOrder(temp, list[j], ascending)) break
swap(j, j + step, list)
j -= step
}
list[j + step] = temp
}
}
steps.forEach(s => shellPass(s))
return list
}
5-归并排序
// sort/merge/index.js
const { pairsInOrder } = require("./compare")
export default function mergeSort(list, ascending) {
const copyList = Array(list.length)
const merge = (low, mid, high) => {
let i = low, j = mid + 1, k = low
while (i <= mid && j <= high) {
copyList[k++] = pairsInOrder(list[i], list[j], ascending) ? list[i++] : list[j++]
}
while (i <= mid) {
copyList[k++] = list[i++]
}
while (j <= high) {
copyList[k++] = list[j++]
}
for (let i = low; i <= high; i++) {
list[i] = copyList[i]
}
}
const split = (low, high) => {
if (high <= low) return
const mid = Math.floor((low + high) / 2)
split(low, mid)
split(mid + 1, high)
merge(low, mid, high)
}
split(0, list.length - 1)
return list
}
6-基数排序
桶排序是一种分类方式的分治思想,排序基数排序是一种特殊的桶排序。设数据规模N,关键字规模M; 当 N >>> M时,桶排序较为适合; 当N <<< M时,基数排序较为适合。基数排序主要包括两个操作:以关键字的范围(桶划分)分布数据,然后根据桶序号并以先进先出的方式收集数据;循环进行上述两个操作。
// sort/radix/index.js
export default function radixSort(list) {
// Char a = 97, use with english char to map for index
if (!list.length) return list
const BASE_CHAR_CODE = 97;
const ENGLISH_ALPHABET_LENGTH = 26;
`` const DIGIT_LENGTH = 10;
const getMostDigit = (list) => {
return Math.floor(Math.log10(Math.max(...list))) + 1
}
const getLongestLength = (list) => {
return list.reduce((acc, s) => {
return s.length > acc ? s.length : acc
}, -Infinity)
}
// 1234 4在第1位,3在第2位,依次类推 nth >= 1
const getIntBucketIndex = (elem, nth) => {
const moded = 10 ** nth
const divided = 10 ** (nth - 1)
return elem < divided ? 0 : Math.floor((elem % moded) / divided)
}
const getCharBucketIndex = (elem, nth, length) => {
if ((length - nth) > elem.length - 1) {
return ENGLISH_ALPHABET_LENGTH - 1
}
const charPos = elem.length < nth ? 0 : elem.length - nth
return element.toLowerCase().charCodeAt(charPos) - BASE_CHAR_CODE
}
const createBuckets = (radix) => {
return Array(radix).fill(null).map(_ => [])
}
const sort = (list) => {
let sortedList = [...list]
const isInteger = Number.isInteger(list[0])
const getBucketIndex = isInteger ? getIntBucketIndex : getCharBucketIndex
const len = isInteger ? getMostDigit(list) : getLongestLength(list)
for (let i = 1; i <= len; i++) {
const buckets = isInteger ? createBuckets(DIGIT_LENGTH) : createBuckets(ENGLISH_ALPHABET_LENGTH)
// distribute
sortedList.forEach(elem => {
const index = getBucketIndex(elem, i)
buckets[index].push(elem)
})
// collect
sortedList = buckets.reduce((acc, bucket) => {
return [...acc, ...bucket]
}, [])}
return sortedList
}
return sort(list)
}