快速排序(用递归实现)

什么是递归?

递归:无限调用自身这个函数,每次调用总会改动一个关键变量,直到这个关键变量达到边界的时候,不再调用。

递归与循环的区别于联系

相同点: 
(1)都是通过控制一个变量的边界(或者多个),来改变多个变量为了得到所需要的值,而反复而执行的; 
(2)都是按照预先设计好的推断实现某一个值求取;(请注意,在这里循环要更注重过程,而递归偏结果一点)

不同点: 

(1)递归通常是逆向思维居多,“递”和“归”不一定容易发现(比较难以理解);而循环从开始条件到结束条件,包括中间循环变量,都需要表达出来(比较简洁明了)。

简单的来说就是:用循环能实现的,递归一般可以实现,但是能用递归实现的,循环不一定能。因为有些题目①只注重循环的结束条件和循环过程,而往往这个结束条件不易表达(也就是说用循环并不好写);②只注重循环的次数而不注重循环的开始条件和结束条件(这个循环更加无从下手了)。

快速排序

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

排序方法:

1、先从数列中选取一个基准数,一般为了方便就选第一个。

2、再对数组进行排序,大的在右边,小的在左边。

3、再分别对左右边重复第一二步,直到各区间只剩下一个数。

 

来个实例:

以一个数组为例,取第一个数为基准

 

 

0123456789
26214565107849891332

 

初始化值,int i=0,j=9,x=a[0];把a[0]赋给x(基准数),相当于给a[0]的位置挖了个坑,把a[0]保存给x以后再用。

先从右即j=9开始循环找出比x小的数,循环到j=8时,a[8]>x,我们就要用a[8]填上a[0]这个坑,a[0]=a[8];   i++;,这时候a[0]的坑填上了但a[8]位置又变成了坑,我们就要从左边进行i循环找到一个比x大的数来填补a[8]这个坑。i++;循环过后,发现a[2]比x大,则a[8]=a[2];  j--;

数组变成了:

 

 

0123456789
13214565107849894532

 

这时,i=2,j=7,x=26。

然后继续重复上述步骤,直到i==j;再将基准数x填入a[i];然后对左右两个子区间进行递归排序,直到区间大小为1时停止。

 

我们来实现一下代码:

public static void sort(int a[], int low, int hight) {
        int i, j, index;
        if (low > hight) {
            return;
        }
        i = low;
        j = hight;
        index = a[i]; // 用子表的第一个记录做基准
        while (i < j) { // 从表的两端交替向中间扫描
            while (i < j && a[j] >= index)
                j--;
            if (i < j)
                a[i++] = a[j];// 用比基准小的记录替换低位记录
            while (i < j && a[i] < index)
                i++;
            if (i < j) // 用比基准大的记录替换高位记录
                a[j--] = a[i];
        }
        a[i] = index;// 将基准数值替换回 a[i]
        sort(a, low, i - 1); // 对低子表进行递归排序
        sort(a, i + 1, hight); // 对高子表进行递归排序

    }

快速排序的优化

对于基准位置的选取一般有三种方法:固定切分,随机切分和三取样切分。固定切分的效率并不是太好,随机切分是常用的一种切分,效率比较高,最坏情况下时间复杂度有可能为O(N2).对于三数取中选择基准点是最理想的一种。

三数取中切分:

public static int partition(int []array,int lo,int hi){
        //三数取中
        int mid=lo+(hi-lo)/2;
        if(array[mid]>array[hi]){
            swap(array[mid],array[hi]);
        }
        if(array[lo]>array[hi]){
            swap(array[lo],array[hi]);
        }
        if(array[mid]>array[lo]){
            swap(array[mid],array[lo]);
        }
        int key=array[lo];
        
        while(lo<hi){
            while(array[hi]>=key&&hi>lo){
                hi--;
            }
            array[lo]=array[hi];
            while(array[lo]<=key&&hi>lo){
                lo++;
            }
            array[hi]=array[lo];
        }
        array[hi]=key;
        return hi;
    }
    
    public static void swap(int a,int b){
        int temp=a;
        a=b;
        b=temp;
    }
    public static void sort(int[] array,int lo ,int hi){
        if(lo>=hi){
            return ;
        }
        int index=partition(array,lo,hi);
        sort(array,lo,index-1);
        sort(array,index+1,hi);
    }

快速排序在序列中元素很少时,效率将比较低,不然插入排序,因此一般在序列中元素很少时使用插入排序,这样可以提高整体效率。

public static void quick(int []array ,int lo,int hi){
        if(hi-lo+1<10){
            insertSort(array);
        }else{
            quickSort(array,lo,hi);
        }
    }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值