首页 > 代码库 > 算法--排序--分治与快速排序

算法--排序--分治与快速排序

学习排序算法也有一段时间,一直没有好好整理下排序算法的相关知识,排序在算法中是最基础也是最重要的,所以有必要自己进行一番整理,在提高领悟算法本质的同时以备以后自己复习,顺便也将自己领悟到的一些思想进行记录。

下面简单整理下快排的算法感悟,当然,个人觉得领悟快排的核心思想分治才是学习这个算法的最重要目的。

一、快排的思想

  1、分治思想。是的,快排最核心的思想就是:递归中重要的思想,分治。好吧,其实刚刚接触分治的时候,或者说递归的时候,我真的真的无法真正理解,感觉就是:好像理解了,但是细想又觉得有困惑。好吧,分治的思想个人觉得最终还是要自己领悟才行的,没有人可以帮得了你,包括我下面对分治一大堆个人理解,估计,对于你真正理解分治可能帮助不大,你得自己去深入思考一下:为什么这样做就可以。ok,下面是个人对分治思想的一些理解:

  A、分解问题,递归解决。主要思想就是将问题分为几个部分,快排这里是两部分,分别对这两部分递归,然后,递归里面又继续递归分解后的两部分(好吧,就是这里,理解这里,分治就理解了,但是,的确有点绕,只能靠自己去领悟了)......直到问题不能再分为止。然后,你会困惑了:利用分治,递归到问题不能分解为止,就可以解决问题了?

  OK,下面说说为什么分治当递归到问题不能分解时就代表递归到头了,也就是问题解决了:这还得回到递归本身,递归是通过递推的关系,在到达某处已经知道结果的时候,停止递归,举个例子:比方说,求阶乘的简单例子:我知道了1的阶乘是1,所以,到达了1就代表我可以停止递归了,因为,1之后的数字的阶乘都可以通过1的阶乘求出来,而递归就是实现这个递推的过程,只不过,它看起来是像从后面倒推到前面,实际上却是从1的阶乘开始求(因为有效的运算是在找到1的阶乘的结果这个返回条件之后即return语句执行结束递归时开始的!)。而分治与递归的区别或许就是分解问题的方式不同:普通递归是一步一步地将问题分解,好像吃蛋糕一小口一小口地吃;而分治就一下子将问题分成两大part或者几大part,然后再在这几个大part里面继续分,就好像,吃蛋糕时,先将蛋糕切成两大部分或几大部分,然后在这两大部分或几大部分进行对应的分割(也不知道有没有形象地说明了,尴尬,个人理解),知道分成可以最后处理的最小结果集(这个结果集已经知道结果了)

  B、好吧,上面一大坨文字废话,其实就是想说明:分治,就是分解问题的过程,将分解后的问题进行递归,直到满足递归停止条件。

  OK,扯太远了,回到刚说那个问题:为什么问题分解到不能分解就代表问题解决了呢?利用快排进行理解吧,那就还得先介绍快排的处理方法:

  2、快排的处理方法:每次让某个元素去到它最终要到达的位置,并且,该元素的左边元素比该元素小,右边元素比该元素大,完成一个过程后,分别对左右两部分进行分治递归。

  OK,接着上面讲:为什么分治到达了问题不能分割之后,就代表解决问题了呢?可以这样理解:当快排中元素只有一个时,那排序本身的结果是知道的,所以这时就是对应递归结束的条件了;同时,前面的将某个元素移到正确位置以及左右元素的顺序确定保证了问题的可递推性(毕竟,递归可以解决问题的前提就是可以找出递推关系),两者结合,使得本来看起来不能递归解决的问题利用递归完美解决了,所以,其实快排指导思想可以概括为:

  递归条件的寻找+递推关系的寻找,前者很容易,就是找到递归边界,对应某个时刻结果已知道(只有一个元素排序结果肯定知道啦);后者,则是通过快排处理排序元素的办法解决:就是这坨办法--每次让某个元素去到它最终要到达的位置,并且,该元素的左边元素比该元素小,右边元素比该元素大,完成一个过程后,分别对左右两部分进行分治递归,这样保证递归时递推关系的满足。

  so,以后如果要用递归解决问题--记得按照上面的思路:找递推成立的可能性处理的办法(数学地讲,就是找递推公示),然后,就是确立递归条件!

(好吧,这一大坨字,仅仅代表个人对递归以及分治背后的理解,各位看不懂,就算了,毕竟的确写得不太好,绕来绕去的(尴尬),最终递归与分治还是要自己去领悟的)

二、下面贴上个人写的快排代码(java版)

//快速排序方法
    public static void sortFast(int [] arr,int l,int r){
        //递归返回条件
        if(l>=r){
            //只有一个元素时返回
            return ;
        }
        //第一轮,将最后一个元素作为标准
        int result = part(arr,l,r);//调用首轮排序方法,将小于arr[r]的元素放在左边,大于该元素的元素放在右边
        //分治递归
        sortFast(arr, l, result-1);
        sortFast(arr, result, r);
    }
    //快速排序的辅助函数
    private static int part(int [] arr,int l,int r){
        //存储r的值
        int v = r;
        l = l-1;
        //进行遍历操作
        int tem;
        for(;;){
            //查找左边符合条件的元素
            while(++l<r){
                if(arr[l]>arr[v]){
                    break;
                }
            }
            
            //查找右边符合条件的元素
            r=r+1;
            while(--r>l){
                if(arr[r]<arr[v]){
                    break;
                }
            }
            //判断两个指针是否相遇
            if(r<=l){
                break;
            }else{
                //执行交换操作
                tem = arr[l];
                arr[l] = arr[r];
                arr[r] = tem;
            }
        }
        //将arr[r]放到正确的的位置中
        tem = arr[v];
        arr[v] = arr[l];
        arr[l] = tem;
        return r;
    }

 

OK,快排的整理到这里,感觉这是篇失败的博客,毕竟,文字太多了,而且,这些文字还是不好理解的.....好吧,递归和分治的领悟,的确有种可意会不可言传的感觉。后面会陆续整理其他排序算法,今天算法分治和快排的整理就这么多吧。

算法--排序--分治与快速排序