快速排序(分治算法)

时间:2025-03-23 10:11:43

快速排序 (分治算法)

基本概念

快速排序可能是应用最广泛的排序算法,适用于各种不同的输入数据且在一般应用中比其他排序都要快的多。

快速排序是一种分治的排序算法。它将一组数组分成两个子数组,将两部分独立地排序,当两个子数组都有序是整个数组也就自然有序了。

  • 原地排序,只需要一个很小的辅助栈。
  • 将长度为 N N N 的数组排序所需时间和 N l g N NlgN NlgN 成正比

步骤

快速排序的基本思想是

  • 先从数列中取出一个数作为基准数
  • 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
  • 再对左右区间重复第二步,直到各区间只有一个数

算法

java普通版

    public class Quick{
        public void sort(int arr[],int low,int high) {
           int l=low;
           int h=high;
           int povit=arr[low];

           while(l<h) {
                while(l<h&&arr[h]>=povit)
                   h--;
               if(l<h){
                   arr[l]=arr[h];
                   l++;
               }

               while(l<h&&arr[l]<=povit)
                   l++;
               if(l<h){
                   arr[h]=arr[l];
                   h--;
               }
           }
            arr[l]=povit;
           print(arr);
           System.out.print("l="+(l+1)+"h="+(h+1)+"povit="+povit+"\n");
           if(l-1>low)sort(arr,low,l-1);
           if(h+1<high)sort(arr,h+1,high);
       }
    }

减少交换次数,提高效率版

private <TextendsComparable<?superT>> voidquickSort(T[]targetArr,intstart,intend){
    int i=start,j=end;
    Tkey=targetArr[start];

    while(i<j){
        /*按j--方向遍历目标数组,直到比key小的值为止*/
        while(j>i&&targetArr[j].compareTo(key)>=0){
            j--;
        }
        if(i<j){
            /*targetArr[i]已经保存在key中,可将后面的数填入*/
            targetArr[i]=targetArr[j];
            i++;
        }
        /*按i++方向遍历目标数组,直到比key大的值为止*/
        while(i<j&&targetArr[i].compareTo(key)<=0){
        /*此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。*/
            i++;
        }
        if(i<j){
            /*targetArr[j]已保存在targetArr[i]中,可将前面的值填入*/
            targetArr[j]=targetArr[i];
            j--;
        }
    }
    /*此时i==j*/
    targetArr[i]=key;//应加判断

    /*递归调用,把key前面的完成排序*/
    this.quickSort(targetArr,start,i-1);

    /*递归调用,把key后面的完成排序*/
    this.quickSort(targetArr,j+1,end);
    //两个递归应加判断
}

例子

举个例子 x[6] = { 64,57,86,42,12,53 } 六个数排序

0 1 2 3 4 5
64 57 86 42 12 53

第一步,开始地址 i = 0 ,结束地址 j = 5 ,基准值 key = 64

key 0 1 2 3 4 5
64 64 57 86 42 12 53

第二步,从后往前找比 key 小的第一个数 x[5] = 53 , i = 0 , j = 5 ,进行交换

key 0 1 2 3 4 5
64 53 57 86 42 12 64

第三步,从前往后找比 key 大的第一个数 x[2] = 86 , i = 2 , j = 5 ,进行交换

key 0 1 2 3 4 5
64 53 57 64 42 12 86

开始重复前面两步

第四步,从 j = 5 开始由后往前找比 key 小的第一个数 x[4] = 12 , i = 2 , j = 5 ,进行交换

key 0 1 2 3 4 5
64 53 57 12 42 64 86

第五步,从 i = 2开始由前往后找比 key 大的第一个数 x[5] = 86 , i = 5 ,j = 5 ,由于 i >= j 使用第一轮结束

64 左边的数都比它小,右边的数都比它大

然后在分别对 [0,3] 和 [5,5] 进行上述操作即可

i = 0 , j = 3key = 53

key 0 1 2 3 4 5
53 53 57 12 42 64 86

从后往前 x[3] = 42 , i = 0 , j = 3

key 0 1 2 3 4 5
53 42 57 12 53 64 86

从前往后 x[1] = 57 , i = 1 , j = 2

key 0 1 2 3 4 5
53 42 53 12 57 64 86

从后往前 x[2] = 12 , i = 1 , j = 2

key 0 1 2 3 4 5
53 42 12 53 57 64 86

[0, 3] 交换完成 [5, 5] 只有一个数,不用交换,自然有序

然后在对 [0, 1] 和 [3,3] 进行操作

i = 0 , j = 1key = 42

key 0 1 2 3 4 5
42 42 12 53 57 64 86

从后往前 x[1] = 12 , i = 0 , j = 1

key 0 1 2 3 4 5
42 12 42 53 57 64 86

[0, 1] 交换完成,整个数组排序完成

0 1 2 3 4 5
12 42 53 57 64 86

总结

快速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。