【数据结构】常见排序算法

匿名 (未验证) 提交于 2019-12-03 00:25:02

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; }

理论

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!