对于快速排序算法的递归栈深度的一点改进

  前几天有人问了我一个关于快速排序的问题。起因是严蔚敏版数据结构第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 }

  另外今天是万圣节。

你可能感兴趣的:(快速排序)