希尔插入排序:
其思想是:将记录按下标的一定增量分组,对每组使用直接插入排序算法排序;减少增量,并重复上述步骤,直到增量序列走完。随着增量逐渐减少,每组包含的数字越来越多,当增量减至1时,整个序列恰被分成一组。
我们称{n/2,(n/2)/2...1}为增量序列,希尔排序的增量序列的选择与证明是个数学难题,以上增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。
Javascript代码:
function shellSort(arr){
var len=arr.length;
//遍历希尔增量序列
for(var fraction=Math.floor(len/2);fraction>0;fraction=Math.floor(fraction/2)){
//直接处理每个增量对应划分下的数组,而不是单独处理
for(var i=fraction;i=0&&arr[j]>arr[j+fraction];j-=fraction){
[arr[j],arr[j+fraction]]=[arr[j+fraction],arr[j]];
}
}
}
console.log(arr);
}
希尔排序的平均时间复杂度为O(n3/2)。由于其以一定的增量来划分数组,因此,其交换过程并不是相邻的元素,故此排序算法不稳定。
归并排序:
其思想是:将原序列拆分成多个子序列,使每个子序列有序,再使子序列段间有序(治)。采用经典的分治策略。
图片的来源https://www.cnblogs.com/chengxiao/p/6194356.html
Javascript实现:
function mergeSort(arr){ //(分)将原数组逐渐拆分,返回的是合并(治)
var len = arr.length;
if (len < 2) {
return arr;
}
var middle = Math.floor(len/2);
var left = arr.slice(0, middle);
var right = arr.slice(middle);
//传到merge中的已经是分别有序的两个数组
return merge(mergeSort(left), mergeSort(right));
}
/*合并过程中考虑三种情况分别对应while和两个if,其一是左右两个数组中都有数值,其二、三是当中有一个没有值*/
function merge(left, right) {
var result = [];
while (left.length>0 && right.length>0) {
//将两个数组中的值挨个的按照大小放入到新数组中
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
if (left.length)
result=result.concat(left);
if (right.length)
result=result.concat(right);
return result;
}
归并排序的最好,最坏,平均时间复杂度均为O(nlogn),但其代价是需要额外的内存空间。其交换最深处也是相邻之间的操作,因此是稳定的排序算法。
快速排序:
其思想是: .在待排序的元素任取一个元素作为基准; 将待排序的元素进行分区,比基准元素大的元素放在它的右边,比其小的放在它的左边;对左右两个分区重复以上步骤直到所有元素都是有序的。是不稳定的排序方式,平均时间复杂度是O(nlgn)。
Javascript代码:
方法一:
function quickSort(arr, left, right) {
if (left == undefined && right == undefined) {
left = 0;
right = arr.length - 1;
}
var left_index = left; //最左边的值的索引
var right_index = right; //最右边的值的索引
var key = 0;
if (left_index < right_index) {
//至少有两个待排序的元素
key = arr[left_index]; //将最左边的值设置为基准值
while (left_index != right_index) {
//从左右两边交替扫描,直到left=right
while (left_index < right_index && arr[right_index] >= key) right_index--; //从右往左扫描,找到第一个比基准元素小的元素
arr[left_index] = arr[right_index];
while (left_index < right_index && arr[left_index] <= key) left_index++; //从左往右扫描,找到第一个比基准元素大的元素
arr[right_index] = arr[left_index];
}
arr[right_index] = key; //基准元素落在当前的空位上
quickSort(arr, left, left_index - 1); //对基准元素左边的元素进行递归排序
quickSort(arr, right_index + 1, right); //对基准元素右边的进行递归排序
}
}
var arr = [12, 15, 46, 78, 15, 0, 1, 485, 54, 1, 12];
quickSort(arr);
console.log(arr);
方法二:
function partition(arr, left, right) {
let mid = left - 1;
for (let i = left; i < right; i++) {
if (arr[i] < arr[right]) {
mid++;
if (mid !== i) {
[arr[mid], arr[i]] = [arr[i], arr[mid]];
}
}
}
mid++;
[arr[mid], arr[right]] = [arr[right], arr[mid]];
return mid;
}
function fastSort(arr, left, right) {
let key = partition(arr, left, right);
if (key > left) fastSort(arr, left, key - 1);
if (key < right) fastSort(arr, key + 1, right);
}
arr = [12, 45, 78, 4, 15, 54, 10, 48, 4];
// fastSort(arr, 0, arr.length - 1);
// console.log(arr);
堆排序:
其思想是:将待排序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆),此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列。