排序算法的总结,怎么选择排序算法

先上各种排序算法的实现,面试经常问的。
冒泡排序

public int[] Maopao(int[] nums){
    int flag=0;
    for(int p=nums.length-1;p>=0;p--){
        int j=0;
        for(;j<p ;j++){
            //每一趟排序都把最大的值放到最后
            if(nums[j]>nums[j+1]){
                flag=1;
                swap(nums,j,j+1);
            }
        }
        if(flag==0)
            break;
    }
    return nums;
}

插入排序

public int[] Insert(int[] nums){
    for(int p=nums.length-1;p>=0;p--){
        int tmp=nums[p];
        int j=p-1;
        for(;j>=0&&nums[j]>tmp;j--){
            nums[j+1]=nums[j];
        }
        nums[j+1]=tmp;
    }
    return nums;
}

希尔排序

public int[] Shell(int[] nums){
    for(int h=nums.length/2;h>=1;h/=2){
        for(int i=0;i<h;i++){
            for(int j=i;j+h<nums.length;j+=h){
                if(nums[j]>nums[j+h])
                    swap(nums,j,j+h);
            }
        }
    }
    return nums;
}

堆排序

public int[] Dui(int[] nums){
    //把nums变成一个大顶堆
    //如果是奇数,说明有右儿子,如果是偶数,说明没有右儿子
    int i=nums.length%2==0?(nums.length/2-1):((nums.length-1)/2-1);
    for(;i>=0;i--){//从最后一个元素的儿子开始,建最大堆
        percDown(nums,i,nums.length-1);
    }
    //把root放到数组的最后一个,再维持大顶堆
    for(int j=nums.length-1;j>=0;j--){
        swap(nums,j,0);
        percDown(nums,0,j-1);
    }
    return nums;
}
public void percDown(int[] nums,int hole,int N){//N是结束比较的最后一个点
    int tmp=nums[hole];
    while((hole+1)*2-1<=N){
        //当左儿子满足范围
        //如果有右儿子并且右儿子比左儿子大,那么child是右儿子
        int child = (hole+1)*2-1!=N &&( nums[(hole+1)*2]>nums[((hole+1)*2)-1] )?((hole+1) *2):(((hole+1) *2)-1);
        if(nums[child]>tmp){
            nums[hole]=nums[child];//较大的值往上移,如果不比tmp大,说明tmp位置正确
            hole=child;
        }else{
            break;
        }
    }
    nums[hole]=tmp;//忘记这一步
}

归并排序

public int[] Merge_sort(int[] num){
    int[] tmp=new int[num.length];
    m_sort(num,tmp,0,num.length-1);
    return num;
}
public void m_sort(int[] arr1,int[] arr2,int L,int Rend){
    int center;
    if(L<Rend){
        center=(L+Rend)/2;
        m_sort(arr1,arr2,L,center);
        m_sort(arr1,arr2,center+1,Rend);
        merg(arr1,arr2,L,center+1,Rend);
    }
}
public void merg(int[] arr1,int[] arr2,int L,int R,int Rend){
    int Lend=R-1;
    int i=L;
    int j=R;
    int num=Rend-L+1;
    int start=i;
    while(i<=Lend&&j<=Rend){
        if(arr1[i]<arr1[j])
            arr2[start++]=arr1[i++];
        else{
            arr2[start++]=arr1[j++];
        }
    }
    while(i<=Lend)
        arr2[start++]=arr1[i++];
    while(j<=Rend)
        arr2[start++]=arr1[j++];
    for(int n=0;n<num;n++){
        arr1[Rend]=arr2[Rend];
        Rend--;
    }
}

快速排序,手撕被问了两次

public int[] Quick_sort(int[] nums){
    q_sort(nums,0,nums.length-1);
    return nums;
}
public void q_sort(int[] arr,int left,int right){
    if(left>right)return;
    int pivot=median(arr,left,right);
    int i=left,j=right-1;
    int tmp;
    while (i<j){
        while(++i<j&&arr[i]<pivot){}
        while(--j>i&&arr[j]>pivot){}
        //此时的ij刚好停在两个错误数的索引上
        if(i<j){
            tmp=arr[i];
            arr[i]=arr[j];
            arr[j]=tmp;
        }
    }
    //然后把pivot放在a[i]的位置上
    tmp=arr[i];
    arr[i]=pivot;
    arr[right-1]=tmp;
    //此时pivot放在正确位置上。
    q_sort(arr,left,i-1);
    q_sort(arr,i+1,right);
    //左右两边递归结束的时候,就全部排好序了。
}
public int median(int[] nums,int left,int right){
        int center=(left+right)/2;
        //左中右的顺序不要乱,左大于中,左大于右,中大于右,保证不出错
        if(nums[left]>nums[center])
            swap(nums,left,center);
        if(nums[left]>nums[right])
            swap(nums,left,right);
        if(nums[center]>nums[right])
            swap(nums,center,right);
        swap(nums,center,right-1);
        return nums[right-1];
    }
public void swap(int[] nums,int i,int j){
        int tmp=nums[j];
        nums[j]=nums[i];
        nums[i]=tmp;
    }

考虑如何选择排序算法,无非还是从(最好最坏)时间空间复杂度、稳定性、初始状态上来考虑。

  • 如果数据初始状态就是无序的,可以从数据的规模来考虑:
    数据量少,二次规模的时候,可以直接用插入排序,因为数据量少的,选择O(N2)复杂度的也可以接受。数据量大的时候,就要选择时间复杂度更优的算法,比如 归并、快排、堆时间复杂度为O(NlogN)的算法。
    如果数据是有范围的,可以用桶排序或基数排序来实现,时间复杂度O(N)(这里忽略了桶的个数,应该加上)
  • 如果数据已经基本有序
    对于冒泡排序和插入排序,如果数据已经基本有序,时间复杂度接近O(N);而希尔排序仍然是O(N2)
    不管是否有序,桶排序和归并排序都是O(NlogN)
  • 如果使用快速排序,而每次选择的pivot值都是剩余数据里的最大值或最小值的画,则快速排序时间复杂度退化到O(N2)。
  • 如果是排名之类的,需要保证稳定性的排序
    则只能选择归并、插入、冒泡这些稳定性好的算法。
原创文章 64 获赞 27 访问量 9404

猜你喜欢

转载自blog.csdn.net/weixin_44893585/article/details/105874651