各种排序方法的JS实现
各种排序算法的对比总结如下表所示:
冒泡排序:
它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
冒泡排序算法的运作如下:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,到最后一对比较完之后最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:C_{min}=n-1,M_{min}=0。
所以,冒泡排序最好的时间复杂度为O(n),冒泡排序的最坏时间复杂度为O(n^2)。综上,因此冒泡排序总的平均时间复杂度为O(n^2)。
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,
我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,
所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。
function bubbleSort(arr) { var flag=false; for(var i=0;i<arr.length-1;i++){ for(var j=0;j<arr.length-1-i;j++){ if(arr[j]>arr[j+1]){ var temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; flag=true; } } if(flag){ flag=false; }else{ break; } } /* 输出结果 */ for (var k = 0; k < arr.length; k++) { document.write(arr[k] + ","); } document.write("<br />"); /* 输出结果结束 */ }
选择排序:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,然后和最小或者最大的位置的元素交换位置,直到所有元素排完。 选择排序是不稳定的排序方法。
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [76 97 65 49 ]
第五趟排序后 13 27 38 49 49 [97 65 76]
第六趟排序后 13 27 38 49 49 65 [97 76]
第七趟排序后 13 27 38 49 49 65 76 [97]
最后排序结果 13 27 38 49 49 65 76 97
选择排序的交换操作介于 0 和 (n - 1) 次之间。选择排序的比较操作为 n (n - 1) / 2 次之间。选择排序的赋值操作介于 0 和 3 (n - 1) 次之间。
比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。交换次数O(n),最好情况是,已经有序,交换0次;
最坏情况是,逆序,交换n-1次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。
function selectSort(array) { var min, temp; ; for (var i = 0; i < array.length; i++) { min = i; for (var j = i + 1; j < array.length; j++) { if (array[min] > array[j]) min = j; } if (min != i) { temp = array[i]; array[i] = array[min]; array[min] = temp; } /* 输出结果 */ document.write("第 + i + "遍排序的结果是:") for (var n = 0; n < array.length; n++) { document.write(array[n] + ","); } document.write("<br />") /* 输出结果结束 */ } }
直接插入排序:
function insertSort(array) { var temp; for (var i = 1; i < array.length; i++) { var temp = array[i]; for (var j = i; j > 0; j--) { if(temp < array[j - 1]){ array[j] = array[j - 1]; } } array[j] = temp; document.write("第?" + i + "遍排序的结果是:") for (var n = 0; n < array.length; n++) { document.write(array[n] + ","); } document.write("<br />"); } }
快速排序:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
值得注意的是,快速排序是一种不稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
var count = 0; function quickSort(array, low, high) { var temp; if (low < high) { var keypoint = QuickSortHelp(array, low, high); count++; document.write("<br />第" + count + "遍排序的结果是:") for (var l = 0; l < array.length; l++) { document.write(array[l] + ","); } quickSort(array, low, keypoint - 1); quickSort(array, keypoint + 1, high); } } function QuickSortHelp(array, low, high) { while (low < high) { while (low < high && array[low] <= array[high]) { high--; } temp = array[low]; array[low] = array[high]; array[high] = temp; while (low < high && array[low] <= array[high]) { low++ } temp = array[low]; array[low] = array[high]; array[high] = temp; } return low; }
堆排序:
堆的定义:n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
function heapSort(array) { var temp; var i; for (i = Math.floor(array.length / 2); i >= 0; i--) { heapAdjust(array, i, array.length - 1); //将数组array构建成一个大顶堆 } for (i = array.length - 1; i >= 0; i--) { /*把根节点交换出去*/ temp = array[i]; array[i] = array[0]; array[0] = temp; /*余下的数组继续构建成大顶堆*/ heapAdjust(array, 0, i - 1); /* 输出结果 */ document.write("<br />第 + (array.length - i).toString() + "遍排序的结果是:") for (var n = 0; n < array.length; n++) { document.write(array[n] + ","); } /* 输出结果结束 */ } } //要调整的子树 //start为数组开始下标 //max是数组结束下标 function heapAdjust(array, start, max) { var temp, j; temp = array[start];//temp是根节点的值 for (j = 2 * start; j < max; j *= 2) { if (j < max && array[j] < array[j + 1]) { //取得较大孩子的下标 ++j; } if (temp >= array[j]) break; array[start] = array[j]; start = j; } array[start] = temp; }
希尔排序:
希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
function shallSort(array) { var increment = array.length; var i var temp; //暂存 var count = 0; do { increment = Math.floor(increment / 3) + 1; for (i = increment; i < array.length; i++) { if (array[i] < array[i - increment]) { temp = array[i]; for (var j = i - increment; j > 0 && temp < array[j]; j -= increment) { array[j + increment] = array[j]; } array[j + increment] = temp; /* 输出结果 */ count++; document.write("<br />第 + count + "遍排序的结果是:") for (var n = 0; n < array.length; n++) { document.write(array[n] + ","); } /* 输出结果结束 */ } } } while (increment > 1) }
归并排序:
//source源数组 //dest目标数组 //s起始下标 //t目标下标 function mSort(source, dest, s, t) { var m; //取中间值 var dest2 = new Array(); if (s == t) { dest[s] = source[s]; } else { m = Math.floor((s + t) / 2); mSort(source, dest2, s, m); mSort(source, dest2, m+1 , t); merge(dest2, dest, s, m, t); /* 输出结果 */ document.write("<br />第 + ++count + "遍排序的结果是:") for (var n = 0; n < dest.length; n++) { document.write(array[n] + ","); } /* 输出结果结束 */ } } //将两个数组按照从小到大的顺序融合 //source原数组 //dest排序后的数组 //s第一个下标 //m第二个数组下标 //总长度 function merge(source, dest, s, m, n) { for (var j = m+1, k = s; j <= n && s <= m; k++) { if (source[s] < source[j]) { dest[k] = source[s++]; } else { dest[k] = source[j++]; } } //将剩余排不完的有序数组加入到dest的末端 if (s <= m) { for (var l = 0; l <= m - s; l++) { dest[k + l] = source[s+l]; } } if (j <= n) { for (var l = 0; l <= n - j; l++) { dest[k + l] = source[j+l]; } } }