一、插入排序
<script>
/*
原理:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用inplace排序(即只需用到O(1)的额外空间排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
步骤:
1.从第一个元素开始,该元素可以认为已经被排序;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3.如果该元素(已排序)大于新元素,将该元素移到下一位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后;
6.重复步骤2-5
*/
var arr=[3,1,5,4,2,9,8,6];
function insertionSort(array){
if(Object.prototype.toString.call(array).slice(8,-1)==='Array'){
for(var i=1;i<array.length;i++){
var key=arr[i];//新元素
var j=i-1;//前一位的位置
while(j>=0&&array[j]>key){//该元素大于新元素
array[j+1]=array[j];//该元素向后挪一位
j--;//换前一个去进行比较
}
array[j+1]=key;//将新元素插入到对应的位置
}
return array;
}else{
return 'array is not an Array';
}
}
console.log(insertionSort(arr))
/*
改进插入排序:查找插入位置时使用二分法查找的方式
*/
function binaryinsertionSort(array){
if(Object.prototype.toString.call(array).slice(8,-1)==='Array'){
for(var i=1;i<array.length;i++){
var key=array[i],left=0,right=i-1;//left,right表示位置
while(left<=right){
var middle=parseInt((left+right)/2);//找中间位置
if(key<array[middle]){
right=middle-1;
}else{
left=middle+1;//left的位置就是我们想要找的位置
}
}
for(var j=i-1;j>=left;j--){
array[j-1]=array[j];//left后面的所有都往后面挪一位
}
array[left]=key;//把新元素插入
}
return array;
}else{
return 'array is not an Array';
}
}
console.log(binaryinsertionSort(arr))
</script>
效果演示:
二、快速排序
/*
原理:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录进行排序,已达到整个序列有序。
步骤:
快速排序使用分治法来把一个串分为两个子串
1.从数列中挑出一个元素,称为“基准”;
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区操作;
3.递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。
*/
var arr=[3,1,5,4,2,9,8,6];
function quickSort(array,left,right){
if(Object.prototype.toString.call(array).slice(8,-1)==='Array'&& typeof left==='Number' && typeof right==='Number'){
if(left<right){
var x=array[right],i=left-1,temp;
for(var j=left;j<right;j++){
if(array[j]<=x){
i++;
temp=array[i];
array[i]=array[j];
array[j]=temp;
}
}
quickSort(array,left,i-1);
quickSort(array,i+1,right);
}
return array;
}else{
return 'array is not an Array or left or right is not a number'
}
}
function quickSort2(arr){
if(arr.length<=1){
return arr;
}
var pivotIndex=Math.floor(arr.length/2);
var pivot=arr.splice(pivotIndex,1)[0];
var left=[];
var right=[];
for(var i=0;i<arr.length;i++){
if(arr[i]<pivot){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort2(left).concat([pivot],quickSort2(right));
}
console.log(quickSort2(arr))
</script>
效果演示:
三、选择排序
<script>
/*
原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
步骤:
1.初始状态:无序区为R[1..n],有序区为空;
2.第i趟排序(i=1,2,3...n-1)开始时,当前有序区和无序区分别为R[1...i-1]和R[i...n]。该趟排序从当前无序区中选出最小的记录R[k],将它与无序区的第1个记录R交换,使R[1...i]和R[i+1..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
3.n-1趟结束,数组有序化了。
*/
var arr=[3,1,5,4,2,9,8,6];
function selectionSort(arr){
var len=arr.length;
var minIndex,temp;
for(var i=0;i<len-1;i++){
minIndex=i;
for(var j=i+1;j<len;j++){
if(arr[j]<arr[minIndex]){//寻找最小的数
minIndex=j;//将最小数的索引保存,方便下面使用
}
}
temp=arr[i];//交换位置
arr[i]=arr[minIndex];
arr[minIndex]=temp;
}
return arr;
}
console.log(selectionSort(arr));
</script>
效果演示:
四、冒泡排序
<script>
/*
原理:
1.比较相邻的元素,如果第一个比第二个大,就交换它们两个;
2.对每一对相邻的元素做同样的工作,从开始第一对到结尾最后一对,这样在最后的元素应该会是最大的数;
3.针对所有的元素重复以后以上的步骤,除了最后一个
4.重复步骤1-3,直到排序完成
*/
var arr=[3,1,5,4,2,9,8,6];
function bubbleSort(arr){
var len=arr.length;
for(var i=0;i<len;i++){
for(var j=0;j<len-1-i;j++){
if(arr[j]>arr[j+1]){//相邻两个元素进行比较
var temp=arr[j+1];//前后交换位置,大的到后面
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
/*
改进一:
设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。
*/
function bubbleSort2(arr){
var i=arr.length-1;//初始时,最后位置保持不变
while(i>0){
var pos=0;//每趟开始时无记录交换
for(var j=0;j<i;j++){
if(arr[j]>arr[j+1]){
pos=j;//记录交换的位置
var temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
i=pos;//为下一趟排序做准备
}
return arr;
}
console.log(bubbleSort2(arr));
/*
改进二:
传统冒泡排序每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法,一次可以得到两个最终值(最大值和最小值),从而使排序趟数几乎减少一半。
*/
function bubbleSort3(arr){
var low=0;
var high=arr.length-1;//设置变量的初始值
var tmp,j;
while(low<high){
for(j=low;j<high;++j)//正向冒泡找到最大值
if(arr[j]>arr[j+1]){
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
--high;//修改high值,前移一位
for(j=high;j>low;--j)//反向冒泡找到最小者
if(arr[j]<arr[j-1]){
tmp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=tmp;
}
++low;
}
return arr;
}
console.log(bubbleSort3(arr))
</script>
效果演示:
五、计数排序
<script>
/*
介绍:计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。
步骤:
1.找出待排序的数组中最大和最小的元素;
2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
*/
function countingSort(array){
var len=array.length,
B=[],
C=[],
min=max=array[0];
for(var i=0;i<len;i++){
min=min<=array[i]?min:array[i];
max=max>=array[i]?max:array[i];
C[array[i]]=C[array[i]]?C[array[i]]+1:1;
}
for(var j=min;j<max;j++){
C[j+1]=(C[j+1]||0)+(C[j]||0);
}
for(var k=len-1;k>=0;k--){
B[C[array[k]]-1]=array[k];
C[array[k]]--;
}
return B;
}
var arr=[3,1,5,4,2,9,8,6];
console.log(countingSort(arr))
</script>
效果演示:
六、堆排序
/*
介绍:堆排序(Heapsort)是指利用对这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
步骤:
1.将初始待排序关键字序列(R1,R2...Rn)构建成大顶堆,此堆为初始的无序区;
2.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2...Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
3.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2...Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2...Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
*/
/*
堆排序
array待排序数组
*/
function heapSort(array){
if(Object.prototype.toString.call(array).slice(8,-1)==='Array'){
//建堆
var heapSize=array.length,temp;
for(var i=Math.floor(heapSize/2)-1;i>=0;i--){
heapify(array,i,heapSize);
}
//堆排序
for(var j=heapSize-1;j>=1;j--){
temp=array[0];
array[0]=array[j];
array[j]=temp;
heapify(array,0,--heapSize);
}
return array;
}else{
return 'array is not an Array!'
}
}
/*
维护堆的性质
arr数组
x 数组下标
len 堆大小
*/
function heapify(arr,x,len){
if(Object.prototype.toString.call(arr).slice(8,-1)==='Array'&&typeof x ==='number'){
var l=2*x+1,r=2*x+2,largest=x,temp;
if(l<len&&arr[l]>arr[largest]){
largest=l;
}
if(r<len&&arr[r]>arr[largest]){
largest=r;
}
if(largest!=x){
temp=arr[x];
arr[x]=arr[largest];
arr[largest]=temp;
heapify(arr,largest,len);
}
}else{
return 'arr is not an Array or x is not a number !'
}
}
var arr=[3,1,5,4,2,9,8,6];
console.log(heapSort(arr))
</script>
效果演示:
七、桶排序
<script>
/*
原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)
步骤:
1.设置一个定量的数组当作空桶;
2.遍历输入数据,并且把数据一个一个放到对应的桶里去;
3.对每个不是空的桶进行排序;
4.从不是空的桶里把排好序的数据拼接起来。
*/
/*
桶排序
array 数组
num 桶的数量
*/
function bucketSort(array,num){
if(array.length<=1){
return array;
}
var len=array.length,buckets=[],result=[],min=max=array[0],regex='/^[1-9]+[0-9]*$',space,n=0;
num=num||((num>1&®ex.test(num))?num:10);
for(var i=1;i<len;i++){
min=min<=array[i]?min:array[i];
max=max>=array[i]?max:array[i];
}
space=(max-min+1)/num;
for(var j=0;j<len;j++){
var index=Math.floor((array[j]-min)/space);
if(buckets[index]){
var k=buckets[index].length-1;
while(k>=0&&buckets[index][k]>array[j]){
buckets[index][k+1]=buckets[index][k];
k--;
}
buckets[index][k+1]=array[j];
}else{
buckets[index]=[];
buckets[index].push(array[j]);
}
}
while(n<num){
result=result.concat(buckets[n]);
n++;
}
return result;
}
var arr=[3,1,5,4,2,9,8,6];
console.log(bucketSort(arr,4))
</script>
效果演示:
八、基数排序
<script>
/*
介绍:基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。
步骤:
1.取得数组中的最大数,并取得位数;
2.arr为原始数组,从最低位开始取每个位组成radix;
3.对radix进行计数排序(利用计数排序适用于小范围数的特点)
*/
/*
基数排序适用于:
1.数据范围较小,建议在小于1000;
2.每个数值都要大于等于0
arr待排序数组
maxDight最大位数
*/
function radixSort(arr,maxDight){
var mod=10;
var dev=1;
var counter=[];
for(var i=0;i<maxDight;i++,dev*=10,mod*=10){
for(var j=0;j<arr.length;j++){
var bucket=parseInt((arr[j]%mod)/dev);
if(counter[bucket]==null){
counter[bucket]=[];
}
counter[bucket].push(arr[j]);
}
var pos=0;
for(var j=0;j<counter.length;j++){
var value=null;
if(counter[j]!=null){
while((value=counter[j].shift())!=null){
arr[pos++]=value;
}
}
}
}
return arr;
}
var arr=[3,1,5,4,2,9,8,6];
console.log(radixSort(arr,1))
</script>
效果演示:
九、归并排序
<script>
/*
介绍:归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若有两个有序表合并成一个有序表,称为2-路归并。
步骤:
1.把长度为n的输入序列分成两个长度为n/2的子序列;
2.对这两个子序列分别采用归并排序;
3.将两个排序好的子序列合并成一个最终的排序序列。
*/
var arr=[3,1,5,4,2,9,8,6];
function mergeSort(arr){//采用自上而下的递归方法
var len=arr.length;
if(len<2){
return arr;
}
var middle=Math.floor(len/2),
left=arr.slice(0,middle),
right=arr.slice(middle);
return merge(mergeSort(left),mergeSort(right));
}
function merge(left,right){
var result=[];
while(left.length&&right.length){
if(left[0]<=right[0]){
result.push(left.shift());
}else{
result.push(right.shift());
}
}
while(left.length)
result.push(left.shift());
while(right.length)
result.push(right.shift());
return result;
}
console.log(mergeSort(arr))
</script>
效果演示:
十、希尔排序
/*
原理:是简单插入排序的改进版;它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
步骤:
先将整个待排序的记录序列分割成为若干个子序列分别进行直接插入排序。
1.选择一个增量序列t1,t2,...,tk,其中ti>tj,tk=1;
2.按增量序列个数k,对序列进行k趟排序;
3.每趟排序,根据对应的增量ti,将待排序列分割成若干个长度为m的子序列,分别对哥哥子表进行直接插入排序。仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度。
*/
var arr=[3,1,5,4,2,9,8,6];
function shellSort(arr){
var len=arr.length,
temp,
gap=1;
while(gap<len/5){//动态定义间隔序列
gap=gap*5+1;
}
for(gap;gap>0;gap=Math.floor(gap/5)){
for(var i=gap;i<len;i++){
temp=arr[i];
for(var j=i-gap;j>=0&&arr[j]>temp;j-=gap){
arr[j+gap]=arr[j];
}
arr[j+gap]=temp;
}
}
return arr;
}
console.log(shellSort(arr))
</script>
效果演示: