快速排序与归并排序

一、快速排序

1.基本思想

从待排序的关键字中选取一个关键字,将小于该关键字的移到前面,而将大于该关键字的移到后面,结果将待排序记录序列分为两个子表,最后将该关键字插到其分界线处。这个过程称为一趟快速排序。然后对两个子表继续进行快速排序。

2.具体步骤

    1)选择一个基准值(选择最右区间,最右边的数作为基准值)  选择基准值的方法有取边法、随机法和三数取中法。
    2)遍历整个区间,每个数都和基准值做比较,并且发生一定交换。(partition的方法有hover法、挖坑法和前后下标法)
    遍历结束后,使得
    比基准值小的数(包括等于)都在基准值的左边
    比基准值大的数(包括等于)都在基准值的右边
    3)分治算法
    分别对左右两个小区间进行同样的方式处理,直到小区间的size==0或者size==1

切割的算法分析:

1)hover法:

从左边找比基准值大的数,从右边找比基准值小的数。然后交换这两个数,直到begin==end,再将基准值插入到begin中。

2)挖坑法:

先记录基准值,然后从左边选出比基准值大的数,将该数填到基准值的位置上,接着从右边遍历,找出比基准值小的数,填到begin的位置上。直到begin==end,最后将基准值填到begin的位置上。

3)前后下标法:

设置两个下标引用:d和i,保证d前面的数都比基准值小(只要array[i]<pivot,就交换d和i对应的元素)[d,i]无序,直到i==right,则d的位置就是基准值的位置,将arr[d]和arr[right]交换。

3.代码

public class QuickSort {
    private static void quickSort(int[] array){
        quickSortIR(array,0,array.length-1);
    }
//[left,right]
    private static void quickSortIR(int[] array, int left, int right) {
        //终止条件  区间里只有一个或者没有
        if(left>=right){
            return;
        }
        //1.取基准值
       int key=array[right];
        //2.分割
        int pivotIndex=partition3(array,left,right);//返回基准值在的下标

        //[left,pivotIndex-1]:比基准值小的数   [pivotIndex+1,right]:比基准值大的数
        quickSortIR(array,left,pivotIndex-1);
        quickSortIR(array,pivotIndex+1,right);
    }
//分割 hover法  从左边找比基准值大的数,从右边找比基准值小的数。然后交换这两个数,直到begin==end,再将基准值插入到begin中。
    private static int partition1(int[] array, int left, int right) {
        int begin=left;
        int end=right;
        int pivot=array[right];
        while(begin<end){
            while(begin<end&&array[begin]<=pivot){
                begin++;
            }
            while(begin<end&&array[end]>=pivot){
                end--;
            }
            swap(array,begin,end);
        }
        //begin==end  则基准值要插入的位置是array[begin],交换两个数
        swap(array,begin,right);
        return begin;
    }
    //挖坑法  先记录基准值,然后从左边选出比基准值大的数,将该数填到基准值的位置上,接着从右边遍历,找出比基准值小的数,填到begin的位置上。
    //直到begin==end,最后将基准值填到begin的位置上。
    private static int partition2(int[] array,int left,int right){
        int pivot=array[right];
        int begin=left;
        int end=right;
        while(begin<end){
            while(begin<end&&array[begin]<=pivot){
                begin++;
            }
            array[end]=array[begin];
            while(begin<end&&array[end]>=pivot){
                end--;
            }
            array[begin]=array[end];
        }
        array[begin]=pivot;
        return begin;
    }
//前后下标法  设置两个下标引用,d和i,保证d前面的数都比基准值小(只要array[i]<pivot,就交换d和i对应的元素)[d,i]无序,直到i==right,则d的位置就是基准值的位置,将arr[d]和arr[right]交换。
    private static int partition3(int[] array,int left,int right){
        int d=left;
        for(int i=left;i<=right;i++){
            if(array[i]<array[right]){
                swap(array,i,d);
                d++;
            }
        }
        swap(array,d,right);
        return d;
    }
    private static void swap(int[] array, int begin, int end) {
        int temp=array[begin];
        array[begin]=array[end];
        array[end]=temp;
    }

    public static void main(String[] args) {
        int[] array=new int[]{33,4,8,3,10,21,9,4};
        quickSort(array);
        for(int item:array){
            System.out.print(item+" ");
        }
    }
}

取基准值还有三数取中法,取值的代码为:

  //返回三数取中的下标
    private static int sanshuQuZhong(int[] array,int left,int right){
        int mid=left+(right-left)/2;
        if(array[left]>array[right]){
            if(array[left]<array[mid]){
                return left;
            }else if(array[mid]>array[right]){
                return mid;
            }else{
                return right;
            }
        }else{
            if(array[right]<array[mid]){
                return right;
            }else if(array[mid]>array[left]){
                return mid;
            }else{
                return left;
            }
        }
    }
private static void quickSortInnerSan(int[] array, int left, int right) {
        //直到size==1或size==0
        if(left==right){
            return;//size==1
        }
        if(left>right){
            return;//size==0
        }
        //要排序的区间是array[left,right]
        //1.找基准值
        int originIndex=sanshuQuZhong(array,left,right);
        swap(array,originIndex,right);
        //2.遍历整个区间,把区间分为三部分。  pivotIndex:基准值的下标
        int pivotIndex=parition3(array,left,right);
        //比基准值小的[left,pivotIndex-1]
        //比基准值大的[pivotIndex+1,right]
        //3.分治算法
        //处理比基准值小的区间
        quickSortInner(array,left,pivotIndex-1);
        //处理比基准值大的区间
        quickSortInner(array,pivotIndex+1,right);
    }

    4.复杂度与稳定性

             时间复杂度                                          空间复杂度
   最好                平均             最坏             最好         平均        最坏    
   O(nlog(n))     O(nlog(n))    O(n^2)        O(logn)      O(log(n))     O(n)

不稳定

二、归并排序

1.基本思想

将待排序的元素序列分成两个长度相等的子序列,对每一个子序列进行排序,然后将它们合并成一个序列。

2.基本步骤

1)把要排序区间平均切分两部分
2)分治算法,对左右两个区间进行同样方式的排序
    直到
        size==1 已经有序
        size==0  区间没有数了(实际不会走到)
3)合并左右两个有序区间到一个有序区间(需要额外空间)

3.代码

public class mergeSort {
    public static void mergeSort(int[] array){
        int[] extra=new int[array.length];
        mergeInner(array,0,array.length,extra);
    }
    //分治算法,对左右两个区间进行同样方式的排序  [low,high)
    private static void mergeInner(int[] array,int low,int high,int[] extra){
        if(low==high-1||low>=high){//如果区间中只有一个数,或没有数,此时有序。则直接返回
            return;
        }
        //计算左右区间   [low,mid)  右:[mid,high)
        int mid=low+(high-low)/2;
//        分治算法,处理两个小区间
        mergeInner(array,low,mid,extra);
        mergeInner(array,mid,high,extra);
        //两个数组已经有序,下面进行合并两个有序数组
        merge(array,low,mid,high,extra);
    }
        //合并两个有序数组  [low,mid)  [mid,high)
    private static void merge(int[] array, int low, int mid,int high, int[] extra) {
        int x=0;
        int i=low;
        int j=mid;
        while(i<mid&&j<high){
            if(array[i]<=array[j]){
                extra[x++]=array[i++];
            }else{
                extra[x++]=array[j++];
            }
        }
        //有一个数组的所有数都被搬完了
        while(i<mid){
            extra[x++]=array[i++];
        }
        while(j<high){
            extra[x++]=array[j++];
        }
        //搬移 extra[0,high-low)-->array[low,high)
        for(int k=low;k<high;k++){
            array[k]=extra[k-low];
        }
    }

    public static void main(String[] args) {
        int[] array=new int[]{23,1,45,9,8,5,6,3,10};
        mergeNoR(array);
        for(int item:array){
            System.out.print(item+" ");
        }
    }
//非递归版本
    public static void mergeNoR(int[] array){
        int[] extra=new int[array.length];
        for(int i=1;i<array.length;i=i*2){
            for(int j=0;j<array.length;j+=2*i){
                int low=j;
                int mid=j+i;
                if(mid>=array.length){
                    //越界
                    continue;
                }
                int high=mid+i;
                if(high>array.length){
                    high=array.length;
                }
                merge(array,low,mid,high,extra);
            }
        }
    }
}

4.复杂度与稳定性分析

时间复杂度:O(n*log(n))
空间复杂度:O(n)-->extra[array.length](统一开辟的)  合并n长  log(n)调用栈 "+"的关系
稳定

三、各排序算法的应用场景

冒泡排序:优化后的冒泡排序可用于当数据基本有序且数据量较少时。

直接插入排序:在待排序的关键字序列基本有序且数据量较少时。

希尔排序数据量较小且基本有序时。

选择排序数据规模较小时。

堆排序:处理数据量大的情况,数据呈流式输入时用堆排序也很方便。

归并排序数据量较大且要求排序稳定时。

快速排序:处理大量数据时。

排序算法的稳定性:

稳定算法:冒泡排序、直接插入排序、归并排序。

不稳定算法:希尔排序、简单选择排序、堆排、快排

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值