排序算法总结二(JavaScript)

本文详细介绍了三种常见的排序算法:归并排序、快速排序和堆排序。包括它们的时间复杂度、稳定性及实现方式,如自顶向下的归并排序、三路快速排序等,并探讨了不同应用场景下的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、归并排序(自顶向下)

复杂度: O(nlogn)               稳定

保留副本需要开辟额外n个存储空间,以空间换取时间效率

//将左右两部分数组进行归并,保留原数组的一个副本,一个一个比较左右两个部分数组元素的大小,将副本的相应元素放到数组中
function __merge(arr,l,mid,r){
    var copy=[];
    for(var m=l;m<=r;m++){
        copy[m]=arr[m];
    }
    var i=l,j=mid+1;
    for(var k=l;k<=r;k++){
        if(i>mid){              //左边部分已完成归并
            arr[k]=copy[j];
            j++;
        }
        else if(j>r){          // 右边部分已完成归并
            arr[k]=copy[i];
            i++;
        }
        else if(copy[i]<copy[j]){     //左边部分的元素大,将该元素添加到数组中,左边索引 i 右移
            arr[k]=copy[i];
            i++;
        }
        else{
            arr[k]=copy[j];      //右边部分的元素大,将该元素添加到数组中,右边索引 j 右移

            j++;
        }
    }

}

//递归调用归并排序函数对整个数组进行排序
function __mergeSort(arr,l,r){
    if(l>=r){
        return;
    }
    var mid=Math.floor((l+r)/2);
    __mergeSort(arr,l,mid);
    __mergeSort(arr,mid+1,r);
    //若左边最大值比右边最小值大,则进行归并操作,否则表示两部分已有序,不需要再进行归并   
  if(arr[mid]>arr[mid+1]){
        __merge(arr,l,mid,r);
    }
}
function mergeSort(arr){
    var n=arr.length;
    __mergeSort(arr,0,n-1);
    return arr;
}

2、归并排序(自底向上)

function __merge(arr,l,mid,r){
    var copy=[];
    for(var m=l;m<=r;m++){
        copy[m]=arr[m];
    }
    var i=l,j=mid+1;
    for(var k=l;k<=r;k++){
        if(i>mid){
            arr[k]=copy[j];
            j++;
        }
        else if(j>r){
            arr[k]=copy[i];
            i++;
        }
        else if(copy[i]<copy[j]){
            arr[k]=copy[i];
            i++;
        }
        else{
            arr[k]=copy[j];
            j++;
        }
    }

}
//若不进行优化  则可以用来对链表进行排序,因为排序中没有用到数组的下标
function mergeSortBU(arr){
    var n=arr.length;
    var size,i;
    //分轮次进行归并,每轮归并的元素数量为size
    for(size=1;size<n;size+=size){
        //每轮归并分组进行
        for(i=0;i+size<n;i+=size+size){
            //优化:如果两边已经有序就不在进行归并
            if(arr[i+size-1]>arr[i+size]){
                __merge(arr,i,i+size-1,Math.min(i+size+size-1,n-1));   //防止越界
            }
        }
    }
    return arr;
}
3、快速排序(三路快排序)

复杂度: O(nlogn)           不稳定

不需要开辟额外空间

function __quickSort(arr,l,r){
    if(l>=r){
        return;
    }
    //三路: 分为三部分  比参考元素小:arr[l+1....lt] 等于参考元素: arr[lt+1....i] 大于参考元素: arr[gt...r];
    var v=arr[l];     //取第一个元素为参考
    var lt=l;
    var gt=r+1;
    var i=l+1;
    while(i<gt){
        if(arr[i]<v){        //比参考元素小的与lt+1交换
            var temp=arr[i]; arr[i]=arr[lt+1]; arr[lt+1]=temp;
            lt++;
            i++;
        }
        else if(arr[i]>v){     //比参考元素大的与gt-1交换
            var temp2=arr[i]; arr[i]=arr[gt-1]; arr[gt-1]=temp2;
            gt--;
        }
        else{           //与参考元素相等直接右移
            i++;
        }
    }
    //将参考元素放到合适的位置
    var temp1=arr[l];
    arr[l]=arr[lt];
    arr[lt]=temp1;
    //递归对左右两部分进行快排序
    __quickSort(arr,l,lt-1);
    __quickSort(arr,gt,r);
}
function quickSort(arr){
    var n=arr.length;
    __quickSort(arr,0,n-1);
    return arr;
}

4、堆排序

复杂度: O(nlogn)         不稳定

原地堆排序,不需要开辟额外空间

//left child (i)=2*i+1      right child (i)=2*i+2     parent(i)=(i-1)/2
//最后一个非叶子节点的索引: (n-1)/2
//构建最大堆的函数
function shiftDown(arr,n,k){
    //要保证左孩子在n范围内   并且该节点比其左孩子或右孩子中任意一个小
    while((2*k+1)<n && (arr[k]<arr[2*k+1] || (arr[k]<arr[2*k+2]&&2*k+2<n))){
        //有右孩子且右孩子比左孩子大
        if(2*k+2<n&&arr[2*k+1]<arr[2*k+2]){
            [arr[k],arr[2*k+2]]=[arr[2*k+2],arr[k]];
            k=2*k+2;
        }else{            //包含右孩子比左孩子小和没有右孩子两种情况
            [arr[k],arr[2*k+1]]=[arr[2*k+1],arr[k]];
            k=2*k+1;
        }
    }
}
function heapSort(arr){
    var n=arr.length;
    //将数组构建成最大堆
    for(var i=Math.floor((n-1)/2);i>=0;i--){
        shiftDown(arr,n,i);
    }
    for(var j=n-1;j>0;j--){
        //堆顶元素最大,每次将堆顶元素与最后一个元素交换
        [arr[j],arr[0]]=[arr[0],arr[j]];
        //如果待排序元素数大于1,则进行堆重构;等于1说明已经完成排序
        if(j>1){
            shiftDown(arr,j,0);
        }else{
            break;
        }
    }
    return arr;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值