找工作之际,重新学习了常见的排序算法,像看似无害的冒泡排序其实也有很多种优化方案,还有简单粗暴的选择排序,模拟抓牌的插入排序,弥补插入排序缺陷的希尔排序,使用了分治思想的归并排序,有点难搞的快速排序,模拟树状结构的堆排序,以及不按套路出牌的基数排序等。
冒泡排序
常规冒泡排序
1 2 3 4 5 6 7 8 9 10 11
| function bubbleSort(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; } } } return arr; }
|
二次优化后的冒泡排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function bubbleSort2(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; let lastIndex = arr.length - 1; for (let i = 0; i < arr.length - 1; i++) { let flag = true; let pos = lastIndex; for (let j = 0; j < pos; j++) { if (arr[j] > arr[j + 1]) { [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; flag = false; lastIndex = j; } } if (flag) break; } return arr; }
|
三次优化后的冒泡排序,减少 flag 标志位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function bubbleSort3(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; let i = arr.length - 1; while (i > 0) { let lastIndex = 0; for (let j = 0; j < i; j++) { if (arr[j] > arr[j + 1]) { [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; lastIndex = j; } } i = lastIndex; } return arr; }
|
关于冒泡排序的总结,如果最小的数在数组的最后,那么优化与不优化效率一样
选择排序
1 2 3 4 5 6 7 8 9 10 11 12 13
| function selectSort(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; for (let i = 0; i < arr.length - 1; i++) { let minPos = i; for (let j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minPos]) { minPos = j; } } [arr[i], arr[minPos]] = [arr[minPos], arr[i]]; } return arr; }
|
插入排序
常规插入排序
1 2 3 4 5 6 7 8 9 10 11 12 13
| function insertSort(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; for (let i = 1; i < arr.length; i++) { let j = i; let temp = arr[i]; while (j && arr[j - 1] > temp) { arr[j] = arr[j - 1]; j--; } arr[j] = temp; } return arr; }
|
二分查找优化的插入排序
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
| function binarySearch(arr, max, value) { let min = 0; while (min <= max) { let mid = Math.floor((min + max) / 2); if (arr[mid] <= value) { min = mid + 1; } else { max = mid - 1; } } return min; } function insertSort(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; for (let i = 1; i < arr.length; i++) { let j = i; let temp = arr[i]; let pos = binarySearch(arr, i - 1, arr[i]); while (j > pos) { arr[j] = arr[j - 1]; j--; } arr[j] = temp; } return arr; }
|
希尔排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function hillSort(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; for ( let gap = Math.floor(arr.length / 2); gap >= 1; gap = Math.floor(gap / 2) ) { for (let i = gap; i < arr.length; i++) { let j = i; let temp = arr[i]; while (j - gap >= 0 && arr[j - gap] > temp) { arr[j] = arr[j - gap]; j -= gap; } arr[j] = temp; } } return arr; }
|
希尔排序解决了,最小的数在后面需要进行 length-1 次比较 e 情况
归并排序
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
| function mergeSort(arr) { let length = arr.length; if (!Array.isArray(arr) || length < 1) return; if (length == 1) return arr; let mid = length >> 1, leftArr = arr.slice(0, mid), rightArr = arr.slice(mid, length); return merge(mergeSort(leftArr), mergeSort(rightArr)); }
function merge(leftArr, rightArr) { let result = [], lPos = 0, rPos = 0; while (lPos < leftArr.length && rPos < rightArr.length) { if (leftArr[lPos] < rightArr[rPos]) { result.push(leftArr[lPos++]); } else { result.push(rightArr[rPos++]); } } while (lPos < leftArr.length) { result.push(leftArr[lPos++]); } while (rPos < rightArr.length) { result.push(rightArr[rPos++]); } return result; }
|
快速排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function quickSort(arr, start = 0, end = arr.length - 1) { if (start >= end) return arr; let pivot = partition(arr, start, end); quickSort(arr, start, pivot - 1); quickSort(arr, pivot + 1, end); return arr; } function partition(arr, start, end) { let temp = arr[start]; while (start < end) { while (arr[end] >= temp && start < end) { end--; } arr[start] = arr[end]; while (arr[start] < temp && start < end) { start++; } arr[end] = arr[start]; } arr[start] = temp; return start; }
|
快速排序,[2, 1, 1, 3] 可证明不稳定性
排序学累了,看一段民族舞吧
堆排序
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
| function heapSort(arr) { if (!Array.isArray(arr) || arr.length <= 1) return; let size = arr.length; for (let i = Math.floor(size / 2) - 1; i >= 0; i--) { heapify(arr, i, size); } for (let i = size - 1; i > 0; i--) { [arr[0], arr[i]] = [arr[i], arr[0]]; heapify(arr, 0, i); } return arr; }
function heapify(arr, index, size) { let larger = index, leftPos = index * 2 + 1, rightPos = index * 2 + 2; if (leftPos < size && arr[leftPos] > arr[larger]) { larger = leftPos; } if (rightPos < size && arr[rightPos] > arr[larger]) { larger = rightPos; } if (index != larger) { [arr[index], arr[larger]] = [arr[larger], arr[index]]; heapify(arr, larger, size); } }
|
基数排序
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
| function radixSort(array) { let length = array.length; if (!Array.isArray(array) || length <= 1) return; let bucket = [], max = array[0], loop; for (let i = 1; i < length; i++) { if (array[i] > max) { max = array[i]; } } loop = (max + "").length; for (let i = 0; i < 10; i++) { bucket[i] = []; } for (let i = 0; i < loop; i++) { for (let j = 0; j < length; j++) { let str = array[j] + ""; if (str.length >= i + 1) { let k = parseInt(str[str.length - 1 - i]); bucket[k].push(array[j]); } else { bucket[0].push(array[j]); } } array.splice(0, length); for (let i = 0; i < 10; i++) { let t = bucket[i].length; for (let j = 0; j < t; j++) { array.push(bucket[i][j]); } } } return array; }
|
下次更新说明一下各种排序的时间复杂度,空间复杂度和稳定性,还有简单说一说什么场景使用哪种排序方式。
参考:
算法知识总结–CavsZhouyou/Front-End-Interview-Notebook
优雅的 JavaScript 排序算法(ES6)–RayJune/Elegant-JavaScript-Sorting-Algorithms