排序算法 - JavaScript 1. 选择排序 核心思想:在未排序的序列中找到最小(大)的元素,存放到排序序列的起始位置,然后再从剩余未排序的元素中继续寻找最小(大)的元素,然后放到已排序序列的末尾,以此类推,直到所有元素排序完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function selectionSort (arr ) { if (!arr || arr.length < 2 ) return arr let min = 0 const N = arr.length - 1 for (let i = 0 ; i < N; i++) { min = i for (let j = i + 1 ; j <= N; j++) { min = arr[j] < arr[min] ? j : min } const [arr[i], arr[minIndex]] = [arr[minIndex], arr[i] } return arr }
2. 冒泡排序 核心思想:比较相邻的两个元素,比较完成视情况交换位置,一轮下来,一个元素排序好,依次类推,直到排序完成。
普通版 从前到后对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function bubbleSort (arr ) { if (!arr || arr.length < 2 ) return arr const N = arr.length - 1 for (let i = 0 ; i < N; i++) { for (let j = 0 ; j < N - i; j++) { if (arr[j] > arr[j + 1 ]) { const temp = arr[j] arr[j] = arr[j + 1 ] arr[j + 1 ] = temp } } } return arr }
普通版 从后往前比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function bubbleSort (arr ) { if (!arr || arr.length < 2 ) return arr const N = arr.length - 1 for (let i = N; i > 0 ; i--) { for (let j = N; j > N - i; j--) { if (arr[j] < arr[j - 1 ]) { const temp = arr[j] arr[j] = arr[j - 1 ] arr[j - 1 ] = temp } } } return arr }
优化版 记录最后一次更改的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function bubbleSort (arr ) { if (!arr || arr.length < 2 ) return arr let N = arr.length - 1 while (N > 1 ) { let lastChange = 1 for (let j = 0 ; j < N; j++) { if (arr[j] > arr[j + 1 ]) { arr[j] = arr[j] + arr[j + 1 ] arr[j + 1 ] = arr[j] - arr[j + 1 ] arr[j] = arr[j] - arr[j + 1 ] lastChange = j } } N = lastChange } return arr }
3. 插入排序 核心思想:将数据按照一定的顺序一个一个的插入到有序的表中,最终得到序列就是已经排好的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const insertionSort = (arr ) => { if (!arr || arr.length < 2 ) return arr for (let i = 1 ; i < arr.length ; i++) { const temp = arr[i] let j = i while (j > 0 && arr[j - 1 ] > temp) { arr[j] = arr[j - 1 ] j-- } arr[j] = temp } return arr }
4. 快速排序 核心思想:从数组中选择一个元素作为基准,将数组中小于基准的元素放到基准的左边,大于基准的元素放到基准的右边,然后再对左右两边的数组重复上述操作,直到数组完全排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const quickSort = function (arr ) { if (!arr || arr.length < 2 ) return arr const left = [] const right = [] const mid = arr.splice ((arr.length / 2 >> 0 ), 1 )[0 ] for (let i = 0 ; i < arr.length ; i++) { const item = arr[i] item <= mid ? left.push (item) : right.push (item) } return [...quickSort (left), mid, ...quickSort (right)] } console .log (quickSort ([2 , 1 , 3 , 6 , 8 ]))
5. 归并排序 核心思想:将数组分成两部分,分别进行排序,然后将排序好的两部分合并在一起,最终得到的结果就是排序好的数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const process = (left, right ) => { const result = [] while (left.length && right.length ) { if (left[0 ] <= right[0 ]) { result.push (left.shift ()) } else { result.push (right.shift ()) } } while (left.length ) { result.push (left.shift ()) } while (right.length ) { result.push (right.shift ()) } return result } const mergeSort = (arr ) => { if (!arr || arr.length < 2 ) return arr const midIndex = arr.length >> 1 const left = arr.slice (0 , midIndex) const right = arr.slice (midIndex) return merge (mergeSort (left), mergeSort (right)) }
6. 希尔排序 核心思想:将数组按照一定的间隔分成几个子数组,然后对子数组进行插入排序,然后缩小间隔,重复上述操作,直到间隔为1,最后对整个数组进行插入排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const shell = (arr ) => { if (!arr || arr.length < 2 ) { return arr } let gap = Math .floor (arr.length / 2 ) while (gap > 0 ) { for (let i = gap; i < arr.length ; i++) { const temp = arr[i] let j = i while (j > 0 && arr[j - gap] > temp) { arr[j] = arr[j - gap] j -= gap } arr[j] = temp } gap = Math .floor (gap / 2 ) } return arr }
7. 堆排序 核心思想:将数组构建成一个大根堆,然后将堆顶元素与最后一个元素交换,然后将剩余的元素继续构建成一个大根堆,重复上述操作,直到数组完全排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 const heapSort = (arr ) => { if (!arr || arr.length < 2 ) return for (let i = 0 ; i < arr.length ; i++) { heapInsert (arr, i) } let heapSize = arr.length while (heapSize > 0 ) { swap (arr, 0 , --heapSize) heapify (arr, 0 , heapSize) } } function swap (arr, i, j ) { const tmp = arr[i] arr[i] = arr[j] arr[j] = tmp } function heapInsert (arr, index ) { while (arr[index] > arr[Math .floor ((index - 1 ) / 2 )]) { swap (arr, index, Math .floor ((index - 1 ) / 2 )) index = Math .floor ((index - 1 ) / 2 ) } } function heapify (arr, index, heapSize ) { let left = 2 * index + 1 while (left < heapSize) { let largest = left + 1 < heapSize && arr[left + 1 ] > arr[left] ? left + 1 : left largest = arr[index] > arr[largest] ? index : largest if (largest === index) break swap (arr, index, largest) index = largest left = 2 * index + 1 } }
8. 计数排序 核心思想:将数组中的元素作为数组的下标,统计每个元素出现的次数,然后将统计好的数组按照下标顺序依次输出,最终得到的结果就是排序好的数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const countingSort = (arr ) => { if (!arr || arr.length < 2 ) return arr const maxValue = Math .max (...arr) const bucket = new Array (maxValue + 1 ) let sortedIndex = 0 const arrLen = arr.length const bucketLen = maxValue + 1 for (let i = 0 ; i < arrLen; i++) { if (!bucket[arr[i]]) { bucket[arr[i]] = 0 } bucket[arr[i]]++ } for (let j = 0 ; j < bucketLen; j++) { while (bucket[j] > 0 ) { arr[sortedIndex++] = j bucket[j]-- } } return arr }
9. 桶排序 核心思想:将数组中的元素分到不同的桶中,对每个桶中的元素进行排序,然后再将桶中的元素依次取出,即可得到一个有序的数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const bucketSort = (arr, bucketSize ) => { if (!arr || arr.length < 2 ) return arr const len = arr.length const min = Math .min (...arr) const max = Math .max (...arr) const bucketCount = Math .floor ((max - min) / bucketSize) + 1 const buckets = new Array (bucketCount) for (let i = 0 ; i < bucketCount; i++) { buckets[i] = [] } for (let i = 0 ; i < len; i++) { const index = Math .floor ((arr[i] - min) / bucketSize) buckets[index].push (arr[i]) } arr.length = 0 for (let i = 0 ; i < bucketCount; i++) { insertion (buckets[i]) for (let j = 0 ; j < buckets[i].length ; j++) { arr.push (buckets[i][j]) } } return arr }
10. 基数排序 核心思想:将整数按位数切割成不同的数字,然后按每个位数分别比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const radixSort = (arr, maxDigit ) => { if (!arr || arr.length < 2 ) return arr const mod = 10 const dev = 1 for (let i = 0 ; i < maxDigit; i++, dev *= 10 , mod *= 10 ) { const counter = [] for (let j = 0 ; j < arr.length ; j++) { const bucket = parseInt ((arr[j] % mod) / dev) if (!counter[bucket]) { counter[bucket] = [] } counter[bucket].push (arr[j]) } let pos = 0 for (let j = 0 ; j < counter.length ; j++) { let value = null if (counter[j]) { while ((value = counter[j].shift ()) !== undefined ) { arr[pos++] = value } } } } return arr }
11. 二分查找 核心思想:在有序数组中,取中间值与目标值进行比较,如果中间值大于目标值,则在左侧继续查找,如果中间值小于目标值,则在右侧继续查找,如果相等则返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const binarySearch = (arr, target ) => { if (!arr || arr.length < 1 ) return -1 let low = 0 let high = arr.length - 1 while (low <= high) { const mid = Math .floor ((low + high) / 2 ) if (arr[mid] === target) { return mid } else if (arr[mid] > target) { high = mid - 1 } else { low = mid + 1 } } return -1 }