前言:看过好大佬写的排序,终究是写了忘忘了写,发现过一段时间还是会忘记,记录在这里,忘了可以常看看~~有大哥发现是自己的代码,留言call我,加引用
.js文件
const arr = [91, 60, 96, 7, 35, 65, 10, 65, 9, 30, 20, 31, 77, 81, 24]; // 常用函数,数组元素换位置 function swap(arr, q, w) { return [arr[q], arr[w]] = [arr[w], arr[q]] }
1.冒泡排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>冒泡排序</title> </head> <body> </body> </html> <script> // 循环两两比较,交换位置 // 冒泡排序 const arr = [91, 60, 96, 7, 35, 65, 10, 65, 9, 30, 20, 31, 77, 81, 24] // 常用函数,数组元素换位置 function swap(arr, q, w) { return [arr[q], arr[w]] = [arr[w], arr[q]] } // 基本版本 function bubbleSort(arr) { const len = arr.length - 1 for (let i = len; i > 0; i--) { for (let j = 0; j < i; j++) { if (arr[j] > arr[j + 1]) { swap(arr, j, j + 1) } } } return arr } // 设置缓存 // 设置一标志性变量 pos,用于记录每趟排序中最后一次进行交换的位置。 由于 pos 位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到 pos 位置即可。 function bubbleSort2(arr) { let i = arr.length - 1 while (i > 0) { let pos = 0 for (let j = 0; j < i; j++) { if (arr[j] > arr[j + 1]) { pos = j swap(arr, j, j + 1) } } i = pos } return arr } // 双向遍历 function bubbleSort3(arr) { let start = 0 let end = arr.length - 1 while (start < end) { for (let i = start; i < end; i++) { if (arr[i] > arr[i+1]) { swap(arr, i, i+1) } } end -= 1 for (let i = end; i > start ; i--) { if (arr[i] < arr[i - 1]) { swap(arr, i-1, i) } } start += 1 } return arr } // 缓存加双向 function bubbleSort4(arr) { let start = 0 let end = arr.length - 1 while (start < end) { let posStart = 0 let posEnd = 0 for (let i = start; i < end; i++) { if (arr[i] > arr[i+1]) { swap(arr, i, i+1) posEnd = i } } end = posEnd for (let i = end; i > start ; i--) { if (arr[i] < arr[i - 1]) { swap(arr, i-1, i) posStart = i } } start = posStart } return arr } console.log(bubbleSort4(arr)) </script>
2.选择排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>选择排序</title> </head> <body> </body> </html> <script src="./js"></script> <script> // 选择一个元素挨个与数组中元素比较,每次循环选出最小的放到前面 // 最小值 function selectSort(arr) { let len = arr.length let minIndex = 0 if (len <= 1) { return arr } for (let i = 0; i < len - 1; i++) { minIndex = i for (let j = i+1; j < len; j++) { if (arr[j] < arr[minIndex]) { minIndex = j } } swap(arr, i, minIndex) } return arr } // 最大值 function selectSort2(arr) { const len = arr.length let maxIndex = 0 if (len <= 1) { return arr } for (let i = len-1; i > 0; i--) { maxIndex = i for (let j = i-1; j >= 0; j--) { if (arr[j] > arr[maxIndex]) { maxIndex = j } } swap(arr, i, maxIndex) } return arr } console.log(selectSort(arr)) console.log(selectSort2(arr)) </script>
3.插入排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>插入排序</title> </head> <body> </body> <script src="./js"></script> <script> // 默认 a[0] 为已排序数组中的元素,从 arr[1] 开始逐渐往已排序数组中插入元素,从后往前一个个比较,如果待插入元素小于已排序元素,则已排序元素往后移动一位, // 直到待插入元素找到合适的位置并插入已排序数组。经过 n - 1 次这样的循环插入后排序完毕。 function insertSort(arr) { const len = arr.length if (len <= 1) { return arr } for (let i = 1; i < len; i++) { for (let j = i; j > 0; j--) { if (arr[j] < arr[j-1]) { swap(arr, j-1, j) } } } return [...arr] } function insert_sort(arr) { let count = 0 for (let j = arr.length; j > 0; j--) { for (let i = j; i > 0; i--) { if (arr[i] < arr[i - 1]) { [arr[i], arr[i - 1]] = [arr[i - 1], arr[i]] count++ } } if (count === 0) { return } } return arr } let arr1 = [7, 4, 3, 67, 34, 1, 8] let arr2 = [ 1, 3, 4, 7, 8, 34, 67 ] insert_sort(arr1) // [ 1, 3, 4, 7, 8, 34, 67 ] 时间复杂度:O(n^2) insert_sort(arr2) // [ 1, 3, 4, 7, 8, 34, 67 ] 时间复杂度:O(n) console.log(insertSort(arr)) </script> </html>
4.希尔排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>希尔排序</title> </head> <body> </body> <script src="./js"></script> <script> // 插入排序升级版 function shellSort(arr) { const len = arr.length let temp = 0 //增量(一般第一趟取长度的一半向下取整) let gap = Math.floor(len / 2) let j //每一趟过后增量会变成原来的一半并向下取整,可知最后一趟增量肯定是1 //增量为1即实现普通的插入排序,执行完毕整个排序肯定就完成了 //而最后一趟增量为1过完时,增量变成0 //gap > 0是为了控制当增量变成0时,整个排序已完成,停止排序 for (gap; gap > 0; gap = Math.floor(gap / 2)) { //以下开始即普通的插入排序(按分好的组进行插入) //初始i=gap是为了让第gap+1个数和第一个数相比 //i<len是从第gap+1个数开始之后的每一个数都和之前差n个增量的数相比 for (let i = gap; i < len; i++) { //保存当前要拿来对比插入的数(比较数) temp = arr[i] //j=i-gap是将temp和之前差一个增量的数相比,arr[j]即被比较数 //j>=0是按增量向前比,一直比到向前减一个增量没有数 即下标j<0 //arr[j] > temp是判断被比较数是否大于比较数, //如果大于,将被比较数向后移一个增量的位置, //即比较数的位置 i或者j+gap //j -= gap 每次在上次被比较数的基础上向前比差一个增量的数 for (j = i-gap; j>=0 && arr[j]>temp; j-=gap) { //将被比较数向后移一个增量的位置, //即比较数的位置 i或者j+gap arr[j + gap] = arr[j] } arr[j + gap] = temp//插入比较数 } } return arr } console.log(shellSort(arr)) </script> </html>
5.快速排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>快速排序</title> </head> <body> </body> </html> <script> /* * 选择一个基准,根据基准分开 * 进行迭代 * */ const arr = [91, 60, 96, 7, 35, 65, 10, 65, 9, 30, 20, 31, 77, 81, 24] const quickSort = function (arr) { if (arr.length <= 1) { return arr } const pivotIndex = Math.floor(arr.length / 2) const pivot = arr.splice(pivotIndex, 1)[0] const left = [] const right = [] for (let i = 0; i < arr.length; i++) { if (arr[i] < pivot) { left.push(arr[i]) } else { right.push(arr[i]) } } return [...quickSort(left), ...[pivot], ...quickSort(right)] } function quickSort2(arr) { if (arr.length <= 1) { return arr } const pivotIndex = Math.floor(arr.length / 2) const pivot = arr.splice(pivotIndex, 1)[0] const left = arr.filter(q => q < pivot) const right = arr.filter(q => q >= pivot) return [...quickSort2(left), ...[pivot], ...quickSort2(right)] } console.log(quickSort2(arr)) </script>
6.归并排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>归并排序</title> </head> <body> </body> <script src="./js"></script> <script> // 归并排序使用分而治之的思想,以折半的方式来递归/迭代排序元素,利用空间来换时间,做到了时间复杂度 O(n·log(n)) 的同时保持了排序 // 元素的 stable,这让它在一些更考虑排序效率和稳定性,次考虑存储空间的场合非常适用(如数据库内排序,和堆排序相比,归并 // 排序的 stable 是优点)。并且归并排序非常适合于列表排序。 function mergeSort(arr) { // recursion const len = arr.length const mid = Math.floor(len / 2) let left = arr.slice(0, mid) let right = arr.slice(mid) if (len < 2) { return arr } return merge(mergeSort(left), mergeSort(right)) } function merge(left, right) { let result = [] while (left.length > 0 && right.length > 0) { // sorting: small to big result.push(left[0] <= right[0] ? left.shift() : right.shift()) // keep stable } return result.concat(left, right) } // test let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48] console.log(mergeSort(arr)) // [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50] </script> </html>
7.堆排序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>堆排序</title> </head> <body style=" 1200px;margin: 0 auto"> <p> 一,堆的概念 堆是一棵顺序存储的二叉树 其中每个节点的关键字都不大于其子节点的关键字,这样的堆称为小根堆 其中每个节点的关键字都不小于其自己点的关键字,这样的堆称为大根堆 下面是一个典型的小根堆: </p> <img src="./img/典型小根堆.jpg" alt=""> <p> 二,堆的调整 堆的调整是为了保持堆的特性而做的一个操作,对以某一个节点为根的子树做堆调整,其实就是将该根节点进行”下沉操作”(具体是通过和子节点的交换完成的),一直下沉到合适位置,使得刚才的子树满足堆的性质为止。 对大根堆的调整步骤如下: 1,在对应元素A[i],找到左孩子A[left(i)]和右孩子A[right(i)]中最大的那一个,将其下标存储在largest中。 2,如果A[i]已经是最大元素,则程序结束 3,否则,i的某个子节点为最大的元素,将A[largest]与A[i]交换 4,再从交换的子节点开始,重复1,2,3步骤,直至叶子节点,算完成一次堆调整。 做一次堆调整的时间复杂度为lgn,从二叉树的根节点到叶子结点,树的深度为lgn,也就是需要下沉的最大次数为lgn。 三,堆的建立 建堆是一个通过不断的堆调整,使得整个二叉树满足堆性质的操作。在数组中,我们一般从下标n/2的数开始作调整,一直到下标为0的数 疑问:为什么是从n/2的数开始?因为下标大于n/2的数都是叶子节点,其子树已经满足堆的性质,所以不用再调整了。下图为一个小根堆的建立图示: </p> <img src="./img/小根堆建立图.jpg" alt=""> <img src="./img/小根堆建立图2.jpg" alt=""> <p> 四,堆的排序 堆的排序是堆建立完成之后进行的,假设数组存储成大根堆的形式之后 第一次将A[0]与A[n-1]交换,然后将A[0~n-2]数组进行堆恢复操作(做堆调整)。 第二次将A[0]与A[n-2]交换,然后将A[0~n-3]数组进行堆恢复操作(做堆调整)。 重复这样的操作,直到A[0]与A[1]交换,排序结束。 由于每次都是将最大的数并入到后面的有序区间,所以整个数组是有序的,上述步骤产生的是升序排列。一个大根堆排序过程的示意图: </p> <img src="./img/大根堆排序过程示意图.jpg" alt=""> <img src="./img/大根堆排序过程示意图2.jpg" alt=""> <img src="./img/大根堆排序过程示意图3.jpg" alt=""> <img src="./img/大根堆排序过程示意图4.jpg" alt=""> <p> 五,堆排序性能分析 堆排序时间=建堆时间+堆恢复时间 建堆时间:建堆的过程就是堆不断调整的过程,前面分析过一次堆调整的时间复杂度为O(lgn),堆建立过程调整了n/2次,因此时间复杂度是O(nlgn). 堆恢复时间:堆恢复操作共进行了n-1次堆调整操作,因此,时间复杂度是O(nlgn) 综上得:堆排序的最差、最优、平均时间复杂度均是O(nlgn) 不稳定 “空间复杂度”指占内存大小,堆排序每次只对一个元素操作,是就地排序,所以空间复杂度是O(1) </p> </body> <script src="./js"></script> <script> /** * *@Description:堆排序 *@param arr *@author 肖芳 */ function headSort(arr){ buildHeap(arr)//构建堆 let len = arr.length for(let i=len-1;i>0;i--){ swap(arr,0,i)//交换堆的第一个元素和最后一个元素 heapify(arr,i)//调整堆 } return arr } /** * *@Description:创建堆 *@param arr *@author 肖芳 */ function buildHeap(arr){ let len = arr.length if(len === 0) return for(let i=Math.floor(len/2);i>0;i--){ heapify(arr,i) } } /** * *@Description:调整堆 *@param arr 调整数组 *@param i 跟 *@author 肖芳 */ function heapify(arr,i){ const left = 2 * i + 1 const right = 2 * i + 2 let largest = i const len = arr.length if(left <len && arr[left]>arr[largest]){//先判断左节点还否超出 largest=left } if(right <len && arr[right]>largest){//有节点是否超出 找出最大的子节点 largest=right } if(largest !== i){ swap(arr,i,largest)//交换 largrst为i heapify(arr,largest)//递归调整 } } /** * *@Description:交换 *@param arr *@author 肖芳 */ function swap(arr,i,j){ let temp=arr[i] arr[i] = arr[j] arr[j] = temp } </script> </html>
8.基数排序
参考网址:https://www.cnblogs.com/0603ljx/p/4379418.html