zoukankan      html  css  js  c++  java
  • 对于快速排序算法的递归栈深度的一点改进

      前几天有人问了我一个关于快速排序的问题。起因是严蔚敏版数据结构第277页的一句话:“如果改写算法10.7,在一趟排序之后比较分割所得两部分的长度,且先对长度短的子序列中的记录进行快速排序,则栈的最大深度可降为O(logn)。”(注:这里的logn是log2n的简写)。

      因为对于原地排序而言,额外的空间复杂度应该是常数,但由于快速排序的实现一般是递归的方式,所以快速排序的额外空间复杂度不是常数,而是和递归深度相关。

      回到一开始的问题上来,刚看到那句话还觉得奇怪,因为话里的栈的深度是指函数递归调用的深度,这个和处理两个子序列的顺序应该是无关的,除非还在递归方面有其它改进,例如回溯到上上级。顺着这个思路来想,觉得改进栈的最大深度是有可能的。

      这个改进的主要思想应该是:用没处理的子节点代替父节点,以减少栈的深度。

      假设父节点A,子节点B、C。处理完B后,并不直接处理C而是回溯回A,并把C交到A这一级来处理。通过这种方法的确可以将栈的最大深度降到O(logn)。详细的数学证明就不写了。

      下面是具体的实现代码。代码里把两种快速排序都列出来了,以方便对比。实验结果也证实是在递归深度是在logn以内。

      1 /***
      2 *对于快速排序递归函数栈的深度的改进
      3 *author:Allen
      4 *date:2012-11-01
      5 *说明:
      6 *由于只是简单实现算法,对于代码风格没太在意。
      7 *原快速排序算法的代码是从网上找的,并做了一点修改。
      8 */
      9 #include <iostream>
     10 #include<ctime>
     11 using namespace std;
     12 const int a_size = 128;//带排序数列长度
     13 int a[a_size];
     14 void swap(int *pLeft,int *pRight)
     15 {
     16     int temp;
     17     temp = *pLeft;
     18     *pLeft= *pRight;
     19     *pRight = temp;
     20 }
     21 //原快速排序
     22 void qs(int begin,int end)
     23 {
     24     static int deep = 0;//递归深度
     25     static int allnum =0;//函数调用总次数
     26     static int mostDeep = 0;//最大递归深度
     27     ++allnum;
     28     ++deep;
     29     if(deep>mostDeep){
     30         mostDeep=deep;
     31     }
     32     int compare=a[begin], left =begin,right = end;
     33     if(abs(right-left)<=1)
     34     {
     35         --deep;
     36         if(left<right && a[left]>a[right]){
     37             swap(a[right],a[left]);
     38         }
     39         return;
     40     }
     41     while (left <right)
     42     {
     43         while ((left <right) && a[right]>=compare)
     44             right--;
     45         swap(a[left],a[right]);
     46         while ((left <right) &&(a[left] <compare))
     47             left++;
     48         swap(a[right],a[left]);
     49     }
     50     a[right] = compare;
     51     qs(begin,right-1);
     52     qs(right+1,end);
     53     --deep;
     54     if(deep<=0){
     55         cout<<"函数调用总次数:"<<allnum<<endl;
     56         cout<<"最大递归深度数:"<<mostDeep<<endl;
     57     }
     58 }
     59 //改进快排,每次递归函数中还剩一组未排序列,即回溯到上级。
     60 //优先排短的序列
     61 //递归函数参数表里还要保留两个未排序列的参数,返回给上级递归处理
     62 //可以认为是下级递归函数(B)完成一半原来任务(C)后,即将自身的函数栈释放,将另一半任务(D)仍交给上级函数(A)
     63 //由此另一半任务(D)执行时,所使用的函数栈深度和下级递归函数(B)的深度是一样的都是上级函数(A)+1
     64 //但是要注意顶级部分的函数仍然要处理掉C、D两个任务。
     65 void qs(int p_begin,int p_end,int &p_unBegin,int &p_unEnd)
     66 {
     67     static int deep = 0;//递归深度
     68     static int allnum =0;//函数调用总次数
     69     static int mostDeep = 0;//最大递归深度
     70     ++allnum;
     71     ++deep;
     72     if(deep>mostDeep){
     73         mostDeep=deep;
     74     }
     75     //    cout<<"递归深度:"<<deep << ' ' <<p_begin <<' ' <<p_end<<' ' <<p_unBegin<<' ' <<p_unEnd<<' ' <<endl;
     76     int compare=a[p_begin], left =p_begin,right = p_end;
     77     if(abs(right-left)<=1)
     78     {
     79         --deep;
     80         if(left<right && a[left]>a[right]){
     81             swap(a[right],a[left]);
     82         }
     83         p_unBegin=-1;
     84         p_unEnd=-1;
     85         return;
     86     }
     87     while (left <right)
     88     {
     89         while ((left <right) && a[right]>=compare)
     90             --right;
     91         
     92         swap(a[left],a[right]);
     93         while ((left <right) &&(a[left] <compare))
     94             ++left;
     95         
     96         swap(a[right],a[left]);
     97     }
     98     a[right] = compare;
     99     
    100     int newBegin;
    101     int newEnd;
    102     int unBegin=p_begin;
    103     int unEnd=p_end;
    104     
    105     if( (right-1)-p_begin <= p_end-(right+1) ){
    106         newBegin=p_begin;
    107         newEnd=right-1;
    108         p_unBegin=right+1;
    109         p_unEnd=p_end;
    110         qs(newBegin,newEnd,unBegin,unEnd);
    111         while(unBegin!=-1&&unEnd!=-1){
    112             qs(unBegin,unEnd,unBegin,unEnd);
    113         }
    114         if(deep>1){
    115             --deep;
    116             return;
    117         }
    118     }else{
    119         p_unBegin=p_begin;
    120         p_unEnd=right-1;
    121         newBegin=right+1;
    122         newEnd=p_end;
    123         qs(newBegin,newEnd,unBegin,unEnd);
    124         while(unBegin!=-1&&unEnd!=-1){
    125             qs(unBegin,unEnd,unBegin,unEnd);
    126         }    
    127         if(deep>1){
    128             --deep;
    129             return;
    130         }
    131     }        
    132     //另一部分
    133     qs(p_unBegin,p_unEnd,unBegin,unEnd);
    134     while(unBegin!=-1&&unEnd!=-1){
    135         qs(unBegin,unEnd,unBegin,unEnd);
    136     }
    137     --deep;
    138     if(deep<=0){
    139         cout<<"函数调用总次数:"<<allnum<<endl;
    140         cout<<"最大递归深度数:"<<mostDeep<<endl;
    141     }    
    142 }
    143 
    144 void printArray(const int * p_arr,const int size){
    145     for (int i=0;i<size;i++)
    146     {
    147 //        cout<<"array["<<i<<"] = "<<p_arr[i]<< ' ';
    148         cout<< p_arr[i]<< ' ';
    149     }
    150     cout<<endl;
    151 }
    152 
    153 int main()
    154 {    
    155     int i=0;
    156     srand(time(0));
    157     int b[a_size];//原数列数组
    158     for (i =0;i< a_size;i++)
    159     {
    160         b[i] =rand()%a_size;
    161         //    b[i] =a_size-i;
    162         //    b[i] =i;
    163     }
    164     cout<<"排序前"<<endl;
    165     printArray(b,a_size);
    166     for (i= 0;i<a_size;i++)
    167     {
    168         a[i]=b[i];
    169     }
    170     cout<<"原来的排序后"<<endl;
    171     qs(0,a_size-1);
    172     printArray(a,a_size);
    173     //下面为改进的
    174     for (i= 0;i<a_size;i++)
    175     {
    176         a[i]=b[i];
    177     }
    178     cout<<"改进的排序后"<<endl;
    179     int st=0;
    180     int en=a_size-1;
    181     qs(0,a_size-1,st,en);
    182     printArray(a,a_size);
    183     system("pause");
    184     return 0;
    185 }

      另外今天是万圣节。

  • 相关阅读:
    dubbo 在不同协议下携带上下文区别
    innodb使用大字段text,blob的一些优化建议(转)
    Redis的过期策略和内存淘汰策略(转)
    在 Docker 里跑 Java,你必须知道的那些事儿!(转)
    如何在宿主机上执行容器里的jmap,jtack,jstat 命令获取信息(原创)
    操作系统实现线程的几种模式 和 java创建线程的3个方式
    MySQL数据库事务各隔离级别加锁情况--read committed && MVCC
    kafka性能调优(转)
    游戏数值系统
    lua函数回调技巧
  • 原文地址:https://www.cnblogs.com/allen8807/p/2749551.html
Copyright © 2011-2022 走看看