(一)冒泡排序
原理:
比较相邻的元素,如果第一个比第二个大,就交换他们两个;
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对;
在这一点,最后的元素应该会是最大的数;
针对所有的元素重复以上的步骤,除了最后一个;
持续每次对越来越少的元素重复上面的步骤,
直到没有任何一对数字需要比较。
function bubbleSort(arr){
console.time('time:')
var len = arr.length;
for(var j = 0 ; j < len ; j++){//n个元素需要循环n次
for(var k = 0 ; k < len - j - 1 ; k++ ){//每次找到的元素都会排到末尾,所以k每次都从0开始,外层循环每次可以找到一个正确的元素,所以内循环只需要 len-j 次,为了防止数组越界,所以还要减一
if(arr[k] > arr[k+1]){//顺序不对,两个元素交换
var t = arr[k];
arr[k] = arr[k+1];
arr[k+1] = t;
}
}
}
console.timeEnd('time:');
return arr;
}
时间复杂度最好为o(n) 最坏为(n^2) 平均为o(n^2) 空间复杂度为o(1) 稳定
(二)简单选择排序
原理:
简单选择排序的基本思想:给定数组:int[] arr={里面n个数据};
第1趟排序,在待排序数据arr[1]~arr[n]中选出最小的数据,
将它与arrr[1]交换;第2趟,
在待排序数据arr[2]~arr[n]中选出最小的数据,将它与r[2]交换;
以此类推,第i趟在待排序数据arr[i]~arr[n]中选出最小的数据,
将它与r[i]交换,直到全部排序完成。
function selectionSort(arr){
console.time('time:')
var len = arr.length;
for(var i = 0 ; i < len ; i++){//n个元素,就有n个位置,需要循环n次
for(var j = i+1 ; j < len ; j++){//选择排序每次都会找到一个正确的元素放到前面,所以第二层索引从i开始,需要和剩下的所有的元素比较,所以 j < len
if(arr[i] > arr[j]){//i是待比较的位置,分别和未排序的所有元素比较,顺序不对的就交换
var t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
}
console.timeEnd('time:');
return arr;
}
时间复杂度最好为o(n^2) 最坏为(n^2) 平均为o(n^2) 空间复杂度为o(1) 不稳定
(三)直接插入排序
原理:
从第一个元素开始,该元素可以被认为已经被排序,
取出下一个元素,在已经排好序的序列中从后往前扫描,
直到找到小于或者等于该元素的位置,
将该位置后面的所有已排序的元素从后往前依次移一位,
将该元素插入到该位置。
function insertionSort(arr){
var len = arr.length;
for(var i = 1 ; i < len ; i++){//把第一个元素当作已排序序列,所以i从1开始
var t = arr[i];//先保存第i个位置的元素,以免被覆盖
var j = i - 1;//第i个元素的前面的元素是已经排好序的,从后往前比较,所以是i-1
while(t < arr[j] && j >= 0){//带插入元素需要插入到第一个比自己小的元素的前面,所以比待比较元素大的都往后挪一个位置
arr[j+1] = arr[j];
j--;
}
arr[j+1] = t;
}
return arr;
}
时间复杂度最好为o(n) 最坏为(n^2) 平均为o(n^2) 空间复杂度为o(1) 稳定
(四)希尔排序
原理:
选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
按增量序列个数k,对序列进行k 趟排序;
每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
function shellSort(arr){
var len = arr.length,
t,
gap = 1;
console.time('time:');
while(gap < len / 5){//动态计算间隔序列
gap = gap * 5 + 1;
}
for(gap; gap > 0 ; gap = Math.floor(gap/5)){//计算下一个间隔,循环内的算法和直接选择排序一样,只是间隔改为gap,可以理解为把1替换为gap
for(var i = gap ; i < len ; i += gap){
t = arr[i];
for(var j = i - gap; t < arr[j] && j >= 0 ; j -= gap){
arr[j+gap] = arr[j]
}
arr[j+gap] = t;
}
}
console.timeEnd('time:');
return arr;
}
时间复杂度最好为o(nlog2 n) 最坏为(nlog2 n) 平均为o(nlog n) 空间复杂度为o(1) 不稳定
(五)归并排序
原理:
把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
function divide(arr){
var len = arr.length;
if(len < 2 )//数组长度分割到最小了,不用再分了,这里只能是2,和merge的写法有关,同时也是递归的停止条件
return arr;
var middle = Math.floor(len / 2);//求数组的分割点,即数组的中点
var left = arr.slice(0, middle);//分割数组
var right = arr.slice(middle);
return merge(divide(left), divide(right));//数组分割到最小的单位后开始排序和合并
}
function merge(arrL, arrR){
var res = [];
while(arrL.length && arrR.length){
if(arrL[0] > arrR[0]){//传进来的数组的最小长度是1,可以当作有序的,所以只需要分别比较第一个元素
res.push(arrR.shift());
}else{
res.push(arrL.shift());
}
}
if(arrL.length){
res = res.concat(arrL);//把剩下的元素拼接的结果数组的后面
}
if(arrR.length){
res = res.concat(arrR);
}
return res;
}
function mergeSort(arr){
console.time('time:');
var res = divide(arr);//divide()递归地把数组分割
console.timeEnd('time:');
return res;
}
时间复杂度最好为o(n) 最坏为(nlogn) 平均为o(nlogn) 空间复杂度为o(n) 稳定