【js】排序算法

常见排序算法

GitHub源码

插入排序

直接插入

理论

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
insort(arr);
console.log(arr);

function insort(arr){
  for(let i=1;i<arr.length;i++){//从第二个元素开始依次处理
    let temp=arr[i];//待插入值
    let j=i-1;//从其前一个位置开始依次比较
    while(j>=0&&arr[j]>temp){//若比待插入值大,依次向后移
      arr[j+1]=arr[j];
      j--;
    }
    arr[j+1]=temp;//将待插入值插入正确位置
  }
  return arr;
}

折半插入

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
halfinsort(arr);
console.log(arr);

function halfinsort(arr){
  for(let i=1;i<arr.length;i++){//从第二个元素开始依次处理
    let temp=arr[i];//待插入值
    // 折半查找0,i-1范围内temp应处于的位置low
    let low=0,high=i-1;
    while(low<=high){
      let mid=Math.floor((low+high)/2);
      if(arr[mid]>temp){
        high=mid-1;
      }else{
        low=mid+1;
      }
    }
    // 将low,i-1的值依次后移一位
    for(let j=i;j>low;j--){
      arr[j]=arr[j-1];
    }
    arr[low]=temp;//将待插入值插入正确位置
  }
  return arr;
}

Shell排序

理论

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
shellsort(arr);
console.log(arr);

function shellsort(arr){
  let d=Math.floor(arr.length/2);// 第一次增量
  while(d>=1){// 增量最小至1
    console.log(d);
    // 此增量下shell排序,从d+1开始
    for(let i=d;i<arr.length;i++){
      // <=i并且间隔为d的一组进行插入排序
      let j=i-d;
      let temp=arr[i];//待插入值
      while(j>=0&&temp<arr[j]){//若比待插入值大,依次向后移
        arr[j+d]=arr[j];
        j=j-d;
      }
      arr[j+d]=temp;//将待插入值插入正确位置
      console.log(arr);
    }
    d=Math.floor(d/2);// 更新增量
  }
  return arr;
}

交换排序

冒泡排序

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
bubsort(arr);
console.log(arr);

function bubsort(arr){
  let flag=false;
  for(let i=0;i<arr.length-1;i++){//最多len-1趟排序,i表示趟数
    flag=false;//标志位
    for(let j=0;j<arr.length-i-1;j++){//一趟冒泡排序,两两交换,最大沉底
      let temp=arr[j];
      if(temp>arr[j+1]){
        arr[j]=arr[j+1];
        arr[j+1]=temp;
        flag=true;
      }
    }
    // 如果flag不为true,表示已是正确排序,不需要继续下一趟排序
    if(!flag) break;
  }
  return arr;
}

快速排序

理论

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
quicksort(arr,0,arr.length-1);
console.log(arr);

// 分治法
function quicksort(arr,left,right){
  if(left>=right) return;
  let index=quick(arr,left,right);
  quicksort(arr,left,index-1);// 处理左边
  quicksort(arr,index+1,right);// 处理右边
}
function quick(arr,left,right){
  // 子表的一趟快速排序
  let x=arr[left];//基准值
  let i=left;
  let j=right;
  // 当i=j时,已按当前基准分好,获得基准正确位置
  while(i!=j){
    // 从右向左扫描,直至到比基准小,i<j保证最终i=j
    while(arr[j]>=x&&i<j){
      j--;
    }
    // 从左向右扫描,直至到比基准大,i<j保证最终i=j
    while(arr[i]<=x&&i<j){
      i++;
    }
    console.log(i+" "+j);
    // 交换两者
    if(i<j){
      let temp=arr[i];
      arr[i]=arr[j];
      arr[j]=temp;
    }
  }
  // 将基准放到正确位置
  arr[left]=arr[i];
  arr[i]=x;

  return i;
}

选择排序

直接选择

与冒泡法区别:每一趟记录最小值位置,一次交换到位

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
selectsort(arr);
console.log(arr);

function selectsort(arr){
  for(let i=0;i<arr.length-1;i++){//最多len-1趟排序,i表示趟数
    var index=i;// 此趟最小值下标
    for(let j=i+1;j<arr.length;j++){// 遍历
      if(arr[j]<arr[index]) index=j; // 更新最小值下标
    }
    // 交换将此趟最小值放在正确位置
    if(i!=index){
      let temp=arr[i];
      arr[i]=arr[index];
      arr[index]=temp;
    }
  }
  return arr;
}

堆排序

理论

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
heapsort(arr);
console.log(arr);

function heapsort(arr){
  // 1.建立大根堆
  // 从最后一个非叶节点(arr.len/2-1)开始依次调整,直至所有非叶节点都调整完
  for(let i=arr.length/2-1;i>=0;i--){
    adjustHeap(arr,i,arr.length);
  }
  // 2.调整堆结构,即交换顶元素0与末尾元素j
  for(let j=arr.length-1;j>0;j--){
    swap(arr,0,j);// 交换顶元素0与末尾元素j
    adjustHeap(arr,0,j);// 重新调整顶元素位置
  }
  return arr;
}
// 调整大根堆
function adjustHeap(arr,index,len){
  let temp=arr[index];// 当前元素
  for(let i=2*index+1;i<len;i=i*2+1){// 从左子节点开始,直至当前节点所有子节点调整完成
    // 若左子节点小于右子节点,i为index节点的子节点值最大的序号
    if(i+1<len&&arr[i]<arr[i+1]){
      i++;
    }
    // 若子节点最大值大于父节点,父节点更新值,重置新的调整点index,否则调整到位跳出循环
    if(arr[i]>temp){
      arr[index]=arr[i];
      index=i;
    }else {
      break;
    }
  }
  arr[index]=temp;// 将当前元素值放在正确位置
}
// 交换元素
function swap(arr,a,b){
  let temp=arr[a];
  arr[a]=arr[b];
  arr[b]=temp;
}

归并排序

理论

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
mergesort(arr,0,arr.length-1);
console.log(arr);

// 分治法
function mergesort(arr,left,right){
  if(left<right){
    let mid=Math.floor((left+right)/2);
    mergesort(arr,left,mid);// 左边归并排序,使得左子序列有序
    mergesort(arr,mid+1,right);// 右边归并排序,使得右子序列有序
    merge(arr,left,mid,right);// 将两个有序子数组合并操作
  }else{
    return;
  }
}
function merge(arr,left,mid,right){
  console.log(left+" "+mid+" "+right);
  let temparr=[];//临时数组,合并结果
  let i=left;//左序列指针
  let j=mid+1;//右序列指针
  let t=0;//临时数组指针
  // 依次比较将最小的按顺序放入临时数组temparr
  while(i<=mid&&j<=right){
    if(arr[i]<=arr[j]){
      temparr[t++]=arr[i++];
    }else{
      temparr[t++]=arr[j++];
    }
  }
  //若左边剩余,将左边剩余元素填充进temp中
  while(i<=mid){
    temparr[t++]=arr[i++];
  }
  //若右边剩余,将右序列剩余元素填充进temp中
  while(j<=right){
    temparr[t++]=arr[j++];
  }
  t=0;
  //将temparr中的元素全部拷贝到原数组中
  while(left<=right){
    arr[left++]=temparr[t++];
  }
}

基数排序

理论

let arr=[15,9,8,1,4,11,7,12,13,6,5,3,16,2,10,14];
console.log(arr);
radixsort(arr,0,arr.length-1,2);
console.log(arr);

function radixsort(arr,left,right,digit){
  let radix=10;// 基数0-9
  let i=0,j=0;
  let rows=[];// 存放0-9各自的数据
  let cols=[];// 一次排序结果
  // d为arr中元素最大位数,从低位到高位进行d次排序
  for(let d=1;d<=digit;d++){
    // 初始化rows[0-9]数据为0
    for(i=0;i<radix;i++){
      rows[i]=0;
    }
    // 遍历所有元素d位,记录[0-9]各自个数
    for(i=left;i<=right;i++){
      j=getDigit(arr[i],d);
      console.log(arr[i]+" "+j);
      rows[j]++;
    }
    console.log(rows);
    // 将[0-9]更新为右边界索引
    for(i=1;i<radix;i++){
      rows[i]=rows[i-1]+rows[i];
    }
    console.log(rows);
    // 从右向左将数据依次装入cols[]
    for(i=right;i>=left;i--){
      j=getDigit(arr[i],d);// 求出d位数数值
      cols[rows[j]-1]=arr[i];// 放入结果对应位置,rows[j]-1为其索引值
      rows[j]--;// 此位数据索引减一
    }
    console.log(cols);
    // 将此次排序结果更新到arr
    for(i=left,j=0;i<=right;i++,j++){
      arr[i]=cols[j];
    }
    // console.log(arr);
  }
  return arr;
}

// 获取x第d位数,比如x=123,d=1,则返回3
function getDigit(x,d){
  let y=Math.floor(x/Math.pow(10,(d-1)))%10;
  return y;
}

性能比较

时间复杂度-空间复杂度-稳定性

1.稳定性比较

  • 除shell排序之外的所有插入排序、冒泡排序、归并排序、基数排序都是稳定排序
  • shell排序、快速排序、选择排序(直接选择、堆)为非稳定排序

2.时间性能比较

  • O(n2):除shell外的所有插入排序、冒泡排序、直接选择排序
  • O(nlog2n):快速排序、堆排序、归并排序、基数排序
  • 一般结论:快速排序最好,但最差情况下,不如堆排序和归并排序;n很小时,快速排序很差
  • 当序列中的记录基本有序或n较小时,直接插入排序最佳,最好情况O(n)

3.说明

  • 不存在绝对最好的排序方法。实际应结合具体情况选择甚至多种方式结合
  • 基于比较的排序算法(基数排序不是)最坏执行时间一定不小于O(nlog2n)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值