排序算法的理解--快速排序

Try to find out the most foundamental truth you can image and reason up from there. It is hard to think that way, the first principle, but if you want to do something new. Its the best way to think.

快速排序所描述的思想

从最直观的思想入手,我们通过分析词条的思想,来确定出快排的实现。

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

分解思想

仔细阅读这段,我们可以很明显看出完成快排需要两大步骤。
第一,“通过一趟排序将要排序的数据分割成独立的两部分”,并且对所分割的两个部分进一步做出了要求,“其中一部分的所有数据都比另外一部分的所有数据都要小”。为了完成这个功能,我们命名Partition()函数来实现,即我们可以理解为,调用一次(一趟)Paritition()后,要排序的数据将在规定的范围内分成明显的两部分。

第二,“再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行”。可以看出在定义中又递归地进行了定义,简单来说就是上一个问题的规模已经减小,所得到的两个部分可以理解成两个“子问题”,那么我们递归的对“子问题”继续Paritition,直至子问题无法分成两个部分位置,我们的整个数据就能变成有序序列了。
因此,很显然我们要在这里实现一个函数,命名为quickSort(),并且得递归的调用去完成。
翻译成框架就是:

void quickSort(){
       //只要问题还没解决,就得进入递归
        if(子问题还可分为两部分){
              //在规定范围内将数据分为两部分
              Paritition();
              //对Paritition得到的两个部分继续进行划分
              quickSort(一部分);
              quickSort(剩下另一部分);
        }
}

看到这里你可能就会觉得,怎么回事啊?道理我是懂了,别老说什么“一部分”,“另一部分”的,就是怎么样才能明确的去进行划分呢?这就是要继续完善代码的关键了!
怎么样将数据划分成两部分呢,并且还要求一部分的所有数都小于另一部分的所有数?从结果来想象,我们希望Paritition()之后是这样的。
在这里插入图片描述

那我不就是得找到一个分界嘛?找到了这个分界,根据这个分界数,小于分界数的往左边丢,大于的往右边丢就好了。完成了这一步我们就可以顺利的写出Paritition(),它所表现的尽管分界所生成的两个部分还未排序,但是我先将序列一分为二,变成以我分界线为基准的“小数部分”和“大数部分”。

问题成功变成了找一个临界数,对数据进行划分。那临界数的选择也是有讲究的。理论上来说,我们希望选择到适当的数,能使得划分的两个部分长度都差不多。我们可以选择待排序序列的第一个数的值作为分界,但是如果此时这个分界数字很小的话,无法有效地将序列划分为大小差不多的两个部分,这样就会使得算法效率不是很高。因此,我们也可以在序列中任选一个数作为分界来减少这种情况的发生。

从这里我们便可以梳理出这个分界是很重要的,它表现在两个地方。

  1. 通过分界成功使得序列分为了符合要求的两部分。
  2. 根据这个分界,我们能进一步表示出了两部分的范围,继续递归求解子问题。

细化实现

OK,既然我们已经知道了Paritition()的功能情况。现在就开始动手实现。将要在一趟任务中,完成以下内容:
(1) 确定一个分界,这里我们用p来存储arr[begin],选择序列的第一个数作为分界数。
(2)为了不开辟新的空间,我们可以使用双指针,去从两端到中间对序列进行扫描。左边指针确保所扫过的数都小于分界数,右边指针确保所扫过的数都大于分界数,如果不符合要求,就把指针所指向的数“丢”到另外一遍。最终两个指针相遇的地方,就是我们分界数应该在的位置。

int getIndex(int arr[], int begin,int end){
   int p = arr[begin];
   while(begin<end){
       while(begin<end && arr[end]>p){  //符合条件一直往左扫
         end--;
       } 
       arr[begin]=arr[end];
       while(begin<end && arr[begin]<p){ //符合条件一直往右
       begin++;
       }
       arr[end] = arr[begin];
   }
       arr[begin] = p;
       return begin;
}

关键的要求1实现完成了,接下来只需要按照要求放入我们之前的递归框架中就大功告成了。

void quickSort(int arr[],int begin,int end){
       //只要问题还没解决,就得进入递归
        if(子问题还可分为两部分){
              //在规定范围内将数据分为两部分
              int index  =  Paritition(arr,begin,end);
              //对Paritition得到的两个部分继续进行划分
              quickSort(arr,begin,index-1);
              quickSort(arr,index+1,end);
        }
}

调用即可

quickSort(arr,0,arr.length-1);

写在后面

刚开始写总是觉得思路不够清晰,记录下来给自己看。多复盘,总会有效果的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值