实现经典的9种排序算法,分析其时间复杂度以及空间复杂度,并用动画的方式演示。
常见的算法时间复杂度由小到大依次为:
Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n^2)<Ο(n^3)<…<Ο(2^n)<Ο(n!)
步骤:
JavaScript实现:
var insertSort = function(arr){
for(let i=1;i=0;j--){
if(arr[j] > temp){
arr[j+1] = arr[j];
arr[j] = temp;
}else{
break;//找到比temp小的则跳出循环
}
}
arr[j+1] = temp;//在比temp小的值后面插入temp值
}
return arr;
时间复杂度:
最好: O(n)
最坏: O(n^2)
平均: O(n^2)
希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:JavaScript实现:
//希尔排序
var shellSort = function(arr,type,showSort){
var half = parseInt(arr.length/2);
for(let d=half;d>=1;d=parseInt(d/2) ){
for(let i=d;i=0;j-=d){
if(arr[j+d] < arr[j]){
let tem = arr[j+d];
arr[j+d] = arr[j];
arr[j] = tem;
}
}
}
}
return arr;
}
时间复杂度:
最好: O(n*log2n)
最坏: O(n^2)
步骤:
JavaScript实现:
var bubbleSort = function(arr){
for(let i=1;i arr[j+1]){
let tem = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tem;
}
}
}
return arr;
}
时间复杂度:
最好: O(n)
最坏: O(n^2)
步骤:
快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列,又称为分区交换排序。步骤为:
JavaScript实现:
//快速排序
var quickSort = function(arr,type,showSort){
//快速排序
var quick = function(out,first,end){
if(first
时间复杂度:
最好:O(n)
平均: O(n*log2n)
最坏: O(n^2)
选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。
JavaScript实现:
//选择排序
var selectSort = function(arr,type,showSort){
for(let i=0;i
时间复杂度:
最好: O(n^2)
最坏: O(n^2)
JavaScript实现:
//堆排序
var heapSort = function(arr,type,showSort){
var len = arr.length;
//建立堆
var sift = function(out, k, m){
let i = k, j = 2*k+1;
while(j <= m && j!=len){
if(j out[j]){
break;
}else{
let temp = out[i];
out[i] = out[j];
out[j] = temp;
i = j;
j = 2*i+1;
}
}
}
let half = parseInt(len/2);
//初始建堆
for(let i=half-1;i>=0;i--){
sift(arr, i, len);
}
for(let i=0;i
时间复杂度:
最好: O(n*log2n)
最坏: O(n*log2n)
JavaScript实现:
//归并排序
var mergeSort = function(arr,type,showSort){
//一次归并算法
var merge = function(out, array, s, m, t){
let i=s, j=m+1, k=s;//i:左边数组的索引,j:右边数组的索引,k:归并结果数组的索引
//没有数组遍历完
while(i<=m && j<=t){
if(out[i] < out[j]){
array[k++] = out[i++];
}else{
array[k++] = out[j++];
}
}
//如果左数组没有遍历完,将左数组剩余数据压入arr
if(i<=m){
while(i<=m){
array[k++] = out[i++];
}
}else{
while(j<=t){
array[k++] = out[j++];
}
}
return array;
}
//一趟归并排序算法
var mergePass = function(out, array, h){
var len = out.length;
let i = 0;
while(i+2*h<=len){
merge(out, array, i , i+h-1, i+2*h-1);
i += 2*h;
}
if(i+h
时间复杂度:
非递归
最好: O(n)
最坏: O(n^2)
原理:
假设待排序的值都在 0~m-1 之间,设置 m 个桶(bucket),首先将值为 i 的值分配(distribute)到第 i 个桶中,然后将各个桶中的记录依次收集(collect)起来。
JavaScript实现:
//桶排序
var bucketSort = function(arr){
var distribute = function(arr){
var bucket = [];
for(let i=0;i=1){
out[index++] = i;
temp--;
}
}
return out;
}
var buckets = distribute(arr);
var out = collect(buckets);
return out;
}
时间复杂度: O(n+m)
空间复杂度: O(m)
基数排序是借助多关键码进行桶排序的思想进行排序。分为最主位优先 (MSD) 和最次位优先 (LSD) 。
以最次位优先为例:
JavaScript实现:
//基数排序
var radixSort = function(arr,type,showSort){
//计算所有数中最大的是几位数
var getMaxPow = function(out){
//求所有数中最大的
var max = 0;
for(let i=0;imax){
max = out[i];
}
}
//计算所有数中最大的是几位数
var max_pow = 1;
while(max>=10){
max_pow++;
max = parseInt(max/10);
}
return max_pow;
}
//升序,分配
var distribute = function(out, pow){
var queue = [];
for(let i=0;i0){
out.push(queue[i].shift());
}
}
return out;
}
var max_pow = getMaxPow(arr);
var queue = [], output = [];
for(let i=0;i
时间复杂度:
最好: O(d*(n+m)) ,其中d是最大数的位数,例如最大123,则d=3
空间复杂度: O(m)
排序
数组长度:
动画时间间隔(ms):