三种简单排序和两种复杂排序 使用JavaScript
首先封装一个列表类,来将插入、排序等操作都封装进去
function arrayList(){
this.array=[];
//插入方法
arrayList.prototype.insert=function(item){
this.array.push(item);
}
//打印方法
arrayList.prototype.print=function(){
console.log(this.array.join("-"));
}
//交换方法(使用次数较多,封装成函数)
arrayList.prototype.swap=function(n1,n2){
//传入下标交换即可
let temp=this.array[n1];
this.array[n1]=this.array[n2];
this.array[n2]=temp;
}
}
简单排序
1、冒泡排序bubbleSort
特点:简单、效率低
思路:越往上气泡越大,对应的越往后数字越大
- 1、从头到尾依次两两比较
- 2、如果后面的数比自己小,就交换,把大的数字像冒泡泡一样往后推
- 3、然后循环,比较到倒数第二个数字为止,直到只剩第一个数
复杂度 比较次数和交换次数都是O(n^2)
arrayList.prototype.bubbleSort=function(){
//外层循环控制内层循环的结束位置
for(let x=length-1; x>0; x--){
//内层循环依次比较、交换
for(let i=0; i<x; i++){
if(this.array[i]>this.array[i+1]){
this.swap(i,i+1);
}
}
}
}
2、选择排序selectionSort
特点:简单、效率高于冒泡排序
思路:每次选择最小的数的索引,依次放到最前面
- 1、从第一个位置开始,与后面所有数比较,选出最小的位置,记录索引
- 2、将第一个位置与最小值所在位置交换
- 3、从第二个开始继续重复上述步骤,直到倒数第二个数
复杂度 比较次数是O(n^2),交换次数是O(n)
arrayList.prototype.selectionSort=function(){
let length=this.array.length;
//从第一个开始依次比较选择最小的值的下标,与第一个交换,内层每次都+1开始循环,外层控制开始索引
for(let i=0;i<length-1;i++){
let min=i;
for(let x=i;x<length;x++){
if(this.array[x]<this.array[min]){
min=x;
}
}
this.swap(i,min);
}
}
3、插入排序insertionSort
特点:简单、效率高于前两者
思路:默认第一个数字有序,之后依次与前面的数比较,保持前面有序
- 1、第二个位置的数,记录数值,与第一个数比较
- 2、比前面的数大就停止,小就将值复制到自己的位置,再往前比较
- 3、循环到最后一个数字为止
复杂度 比较次数是最多O(n^2),复制次数最多是O(n^2),赋值比交换本就效率高
arrayList.prototype.insertionSort=function(){
let length=this.array.length;
//默认第一个数字有序,从第二个位置开始向前比较,如果小于,复制移动,大于停止
//没有确定的内循环次数,内层用while
for(let i=1;i<length;i++){
let x=i;
let temp=this.array[i];
while(this.array[x-1]>temp && x>0){
this.array[x]=this.array[x-1];//前一个位置值后移
x--;
}
this.array[x]=temp;
}
}
复杂排序(复杂度皆不好证明)
1、希尔排序shellSort
特点:是对插入排序的升级 思路:将数据进行分组,组内排序,分组的间隔逐渐减小,当减小到1时就是插入排序,比起插入排序减少了平均比较次数
- 1、以gap=length/2为第一次分组,组内进行插入排序
- 2、完成之后每次gap/2为下一次分组
- 3、直到gap=1为最后一次
arrayList.prototype.shellSort=function(){
let length=this.array.length;
let gap=Math.floor(length/2);
while(gap>=1){
for(let i=gap;i<length;i++){
let x=i;
let temp=this.array[x];
while(this.array[x-gap]>temp && x>=gap){
this.array[x]=this.array[x-gap];
x-=gap;
}
this.array[x]=temp;
}
gap=Math.floor(gap/2);
}
}
2、快速排序quickSort
特点:分而治之 思路:选择一个枢纽,左边放比它小的数,右边放比它大的数,这个位置就是该数字的正确排序位置
- 1、从0、length-1和中间的mid中选择中位数为枢纽,并将三者排序交换位置
- 2、将mid与倒数第二个数交换位置,然后将0设置为left,length-3的位置为right
- 3、两者向中间移动,left遇到大于mid值的停止,right遇到小于停止
- 4、如果此时left<=right,交换二者位置,继续直到left>right
- 5、将right所指的位置与mid现在所在的位置交换,就是该数的正确位置
- 6、使用递归,左边从0,mid-1;右边从mid+1,length-1
//1、寻找枢纽
arrayList.prototype.median=function(left,right){
let center=Math.floor((left+right)/2);
if(this.array[left]>this.array[center]){
this.swap(left,center);
}
if(this.array[right]<this.array[center]){
this.swap(right,center);
}
//将枢纽与倒数第二个数交换,因为最后一个数肯定大于枢纽,不必进入第一次排序
this.swap(center,right-1);
return this.array[right-1];
}
//2-2、快排
arrayList.prototype.quickSort=function(){
this.quick(0,this.array.length-1);
}
arrayList.prototype.quick=function(left,right){//递归的函数
//第一次根据median把小的放左边,大的放右边
if(left>=right){return;}
let pivot=this.median(left,right);
let i=left;
let j=right-1;
while(true){//符合条件直接break
while(this.array[++i]<pivot){}//此时i的位置是大于pivot的数
while(this.array[--j]>pivot){}//此时j的位置是小于pivot的数
if(i<j){
this.swap(i,j);
}else{
break;
}
}
this.swap(i,right-1);//此时i位置上的值就是这个位置的值
//递归,左右各自分而治之
this.quick(left,i-1);
this.quick(i+1,right);
}
总结
快速排序时目前效率最高的排序方式,如果遇到面试或者笔试要求写排序,应当时首选。