统一说明:在所有的算法代码中,为了提高可用性,都加入了
order:表示升序/降序的选择
参数,因为我在实现的时候发现,无论是升序之于降序或是降序之于升序往往都是比较的条件做一个补集,所以这里使用 JS 的函数表达式 来根据不同的order参数
来定义不同的比较函数compare
,然后在具体实现算法中用比较函数做为比较条件的代替,进而达到一份代码完成升序和降序的功能扩展
以下是时间复杂度在O(n^2)级别的排序算法
选择排序
/**
* 选择排序
* 基本思想:先扫描一边序列,找到最小(大)值,将其和第一个元素互换;
* 第二次,扫描n-1次,找到最小(大)值,将其和第2个元素互换
* 依次类推
* 时间复杂度 : O(n^2)
* Method : selectionSort
* params : {
* sort :待排序数组
* n :数组长度
* order : {
* ASCEND : 升序
* DECEND : 降序
* }
* }
* return : sort or -1 //sort是已排序数组,-1是错误返回
*
*/
function selectionSort(sort , n , order){
//对升序和降序做一个区分
var compare = null;
if(order == 'ASCEND'){
compare = function(a,b){
return a < b ;
}
}else if(order == 'DECEND'){
compare = function(a,b){
return a > b;
}
}else return -1;
//主要的思想实现
for(var i=0;i<n-1;i++){
var tag = sort[i];
var index = i;
for(var j=i+1;j<n;j++){
if(compare(tag,sort[j])){
tag = sort[j];
index = j;
}
}
sort[index] = sort[i];
sort[i] = tag;
}
return sort;
}
冒泡排序
/**
* 冒泡排序
* 基本思想:通过比较相邻两个值的大小,如果大(小)则向后(前)排,就像开水壶中的从底向上的气泡一样
* 时间复杂度:O(n^2)
* Method : bubbleSort
* params : {
* sort :待排序数组
* n :数组长度
* order : {
* ASCEND : 升序
* DECEND : 降序
* }
* }
* return : sort or -1//已排序数组
*/
function bubbleSort(sort , n , order){
//定义升序还是降序
var compare = null;
if(order == 'ASCEND'){
compare = function(a,b){
return a > b ;
}
}else if(order == 'DECEND'){
compare = function(a,b){
return a < b;
}
}else return -1;
//主要排序实现代码段
for(var i = 0;i<n;i++){
for(var j=0;j<n-i-1;j++){
if(compare(sort[j],sort[j+1])){
var t = sort[j];
sort[j] = sort[j+1];
sort[j+1] = t;
}
}
}
return sort;
}
插入排序
/**
* 插入排序
* 基本思想:插入排序是一种很直观的方式,思想也很好理解。就是从未排序的序列中依次去新元素,然后去
* 和以排好序的序列从后往前比较,如果该元素(有序序列里的元素)比新元素大(小)则向后移动,当找到
* 比新元素小(大)的元素时,将新元素插入其后位置即可
* 时间复杂度:O(n^2)
* Method : insertSort
* params : {
* sort :待排序数组
* n :数组长度
* order : {
* ASCEND : 升序
* DECEND : 降序
* }
* }
* return : sort or -1//已排序数组
*/
function insertSort(sort , n , order){
//定义升序还是降序
var compare = null;
if(order == 'ASCEND'){
compare = function(a,b){
return a > b ;
}
}else if(order == 'DECEND'){
compare = function(a,b){
return a < b;
}
}else return -1;
//主要的插入排序思想实现代码
var length = sort.length;
var newSort = new Array(length);
newSort[0] = sort[0];
var flag = 1;
for(var i=1;i<length;i++){
var newItem = sort[i];
for(var j=flag-1;j>=0;j--){
if(compare(newSort[j],newItem)){
newSort[j+1] = newSort[j];
}else{
newSort[j+1] = newItem;
break;
}
}
//当前newItem比有序序列中元素都小(大)
if(j==-1){
newSort[0] = newItem;
}
flag++;
}
return newSort;
}
以下是时间复杂度在O(nlogn)级别的排序算法
快速排序
/**
* 快速排序
* 基本思想:先选取基准数,一般是a[0],然后从左右两端向中间遍历找到分裂点
* 然后通过递归左右两边,以相同的方式完成排序
* 时间复杂度 : O(nlogn) [实验表明,当n较大规模时,若输入数组随机,快速排序的算法效率很高]
* Method : quickSort
* params : {
* sort :待排序数组
* n :数组长度
* order : {
* ASCEND : 升序
* DECEND : 降序
* }
* }
* return : sort or -1//已排序数组
*
*/
function quickSort(sort , n, order){
//定义升序还是降序
var compare = null;
if(order == 'ASCEND'){
compare = function(a,b){
return a <= b ;
}
}else if(order == 'DECEND'){
compare = function(a,b){
return a >= b;
}
}else return -1;
//内部的函数定义,主要实现快速排序的代码块
var quickSortR = function(sort , start , end , compare){
if(start >= end){
return;
}else{
var std = sort[start];
var first = start;
var last = end;
while(first < last){
while(first < last && compare(sort[last],std)){
--last;
}
sort[first] = sort[last];
while(first < last && compare(std,sort[first])){
++first;
}
sort[last] = sort[first];
}
sort[first] = std;
quickSortR(sort , start , first-1, compare);
quickSortR(sort , first+1 , end , compare);
}
return sort;
}
return quickSortR(sort , 0 , n-1 , compare);
}
这里需要注意一点就是,从left向右找先开始还是从right向左找先开始,我一开始以为这个不重要,谁先开始都一样,但是事实并非如此。应该先从right向左找,在从left向右找。
归并排序
/**
* 归并排序
* 基本思想:归并排序是一个典型的运用分治法的思想解决问题的实例,通过将规模为n的序列,分为n/2规模
* 并不断分治直到问题规模到可以解决的小时对小规模的序列进行排序并合并左右两个序列来到达排序的目的。
* 通过递归子问题的分治排序再合并,便可以完成整个序列的排序
* 时间复杂度: O(nlogn)
* Method : mergeSort
* params : {
* sort :待排序数组
* n :数组长度
* order : {
* ASCEND : 升序
* DECEND : 降序
* }
* }
* return : sort or -1//以排序数组
*
*/
function mergeSort(sort , n , order){
//定义升序还是降序
var compare = null;
if(order == 'ASCEND'){
compare = function(a,b){
return a < b ;
}
}else if(order == 'DECEND'){
compare = function(a,b){
return a >= b;
}
}else return -1;
//合并函数
var merge = function(sortLeft , sortRight , sortTotal , compare){
var length = sortTotal.length;
var leftLength = sortLeft.length;
var rightLength = sortRight.length;
var i = 0,j=0,l=0;
while(l<length){
if(i==leftLength){
while(j<rightLength){
sortTotal[l] = sortRight[j];
j++;
l++;
}
return sortTotal;
}
if(j==rightLength){
while(i<leftLength){
sortTotal[l] = sortLeft[i];
i++;
l++;
}
return sortTotal;
}
var sortL = sortLeft[i];
var sortR = sortRight[j];
if(compare(sortL,sortR) && i<leftLength){
sortTotal[l] = sortL;
i++;
}
if(!compare(sortL,sortR) && j<rightLength){
sortTotal[l] = sortR;
j++;
}
l++;
}
return sortTotal;
}
//分治法对数组拆分
var mergeSortR = function(sort , n){
if(n>1){
var middle = Math.ceil(n/2);
var sortLeft = new Array(middle);
var sortRight = new Array(n-middle);
for(var i = 0;i<middle;i++){
sortLeft[i] = sort[i];
}
for(var i = 0;i<n-middle;i++){
sortRight[i] = sort[i+middle];
}
//分别递归左右半个序列
mergeSortR(sortLeft , sortLeft.length);
mergeSortR(sortRight , sortRight.length);
return merge(sortLeft,sortRight,sort,compare);
}else return sort;
}
return mergeSortR(sort , n);
}
以下是时间复杂度在接近O(n)级别的排序算法
计数排序
/**
* 计数排序
* 基本思想:计数排序和之前的冒泡、选择、插入、快排、归并等基于比较的排序不同,它的思想是基于统计,
* 就是通过统计序列中相同项的个数,然后根据统计的顺序反向还原数组来排序。但是因为这个统计的方式是
* 通过数组的下标和项之间的互换,所以只能支持自然数集合内的排序也即>=0的整数集合
* 时间复杂度:O(n+k) 其中k表示序列中(最大项-最小项)的值
* Method : countSort
* params : {
* sort :待排序数组
* n :数组长度
* order : {
* ASCEND : 升序
* DECEND : 降序
* }
* }
* return : sort or -1//以排序数组
*/
function countSort(sort , n , order){
//首先要找到最大值和最小值
var min = sort[0];
var max = sort[0];
for(var i=1;i<n;i++){
if(sort[i]<min){
min = sort[i];
}
if(sort[i]>max){
max = sort[i];
}
}
//对待排序数组遍历并统计
var statis = new Array(max+1);
//初始化统计数组每个元素都为-1
for(var i=0;i<statis.length;i++)statis[i]=-1;
//统计
for(var i=0;i<n;i++){
if(statis[sort[i]] == -1)statis[sort[i]]=1;
else statis[sort[i]] += 1;
}
//还原数组,进行排序
var j = 0;
if(order == 'ASCEND'){
for(var i=min;i<=max;i++){
while(statis[i]!=-1 && statis[i]!=0){
statis[i]--;
sort[j] = i;
j++;
}
}
}else if(order == 'DECEND'){
for(var i=max;i>=min;i--){
while(statis[i]!=-1 && statis[i]!=0){
statis[i]--;
sort[j] = i;
j++;
}
}
}else return -1;
return sort;
}