递归到非递归转换——归并排序与快排的非递归实现

 

在高级语言中,函数的参数传递是由栈来实现的,后调用的函数的参数在栈的上部,先调用的函数的参数在栈的下部,在实现递归函数时,每一次调用的参数等信息都会保存在栈中,这样在数据比较在时会出现栈溢出的错误,而且反复调用函数,会使效率变的较低,在VC中对10万个数排序,用递归的快排花了30ms,而用非递归,需要25ms。

       而我们可以很容易的通过使用自定义的栈,来将其转化为全部在一个函数中实现,而无需进行递归函数调用,从而节省了大量时间,而且也可以尽量避免栈举出的问题。

       递归函数一般有两类,一类是先执行函数操作,再递归调用函数自身,一类是先递归调用自身,再执行函数操作,题目所述的两种排序分别属于两种递归调用。

       先说快速排序,首先将它的递归版本给出,每次以最后一个元素为标准,将比它小的元素全部换到前面去,然后把最后一个元素换到最后一个比它小的元素后面,从而实现该元素左面的元素都比它小,而右面的都比它大:

void ksort(int l,int h,int a[])//l为第一个元素下标 h为最后一个元素下标的后一个

{

       int i,j=l;

       int tem;

       if(l>=h)return;

       for(i=l;i<h-1;i++)/一次遍历,将比a[h-1]小的元素都搬到左面,j表示下一次需要交换的/元素的位置

       {

              if(a[i]<a[h-1])如果比a[h-1]小,就搬到左面去

              {

                     tem=a[i];

                     a[i]=a[j];

                     a[j]=tem;

                     j++;

              }

       }

       tem=a[j];//最后将a[h-1]搬到中间去,让不比它小的都在它右面

       a[j]=a[h-1];

       a[h-1]=tem;

       ksort(l,j,a);///递归

       ksort(j+1,h,a);

}

要模拟该递归过程 , 首先要做的就是建立一个栈 , 最简单的方法是用数组 , 建立两个数组模拟的栈 sl[100],sh[100] 分别存放 参数中的每次操作的首元素的位置和尾元素的后一个位置  ,定义变量head表示栈顶指针,

首先将sl[0] 和 sh[0] 分别赋值为 l 和 h , 对于先执行后递归的函数,在每次执行递归的时候 , 将新的参数压入栈中, 然后在入栈之后重新执行新一轮的函数操作, 因为重复执行同样的动作, 所以需要一个循环 ,每个取参数, head 要自减, 所以跳出条件就是head<0:

while(head>=0)

       {

              x=sl[head];

              y=sh[head];

              head--;

              j=x;

              for(i=x;i<y-1;i++)

              {

                     if(a[i]<a[y-1])

                     {

                            tem=a[i];

                            a[i]=a[j];

                            a[j]=tem;

                            j++;

                     }

              }

              tem=a[j];

              a[j]=a[y-1];

              a[y-1]=tem;

              if(x<j)

              {

                     head++;

                     sl[head]=x;

                     sh[head]=j;

              }

              if(j+1<y)

              {

                     head++;

                     sl[head]=j+1;

                     sh[head]=y;

              }

       }

}

类似快速排序这类递归调用的特点是取一次参数执行一次操作,之后这组参数就没有用了,可以被下一组参数所覆盖了,从而不需要很将所有的参数都保存在栈中,再集中进行处理,而归并排序这样的先递归,后操作的,则不可以了,下面是归并排序的递归版代码:

void mergesort(int a[],int l,int h)///此中的h为最后一个元素的位置

{

       if(l>=h)return ;

       int m=(l+h)/2;

       mergesort(a,l,m);

       mergesort(a,m+1,h);经过这两行,l到m和m=1到h,就都是已经有序的序列了,然后//将两个有序序列进行归并

       int *arr=new int[h-l+1];

       int k=0;

       int i=l,j=m+1;

       while(i<=m&&j<=h)

       {

              if(a[i]<a[j])

              {

                     arr[k]=a[i];

                     i++;

                     k++;

              }

              else

              {

                     arr[k]=a[j];

                     j++;

                     k++;

              }

       }

       if(i<=m)

       {

              while(i<=m)

              {

                     arr[k]=a[i];

                     i++;k++;

              }

       }

       if(j<=h)

       {

              while(j<=h)

              {

                     arr[k]=a[j];

                     j++;k++;

              }

       }

       for(i=l;i<=h;i++)

       {

              a[i]=arr[i-l];

       }

       delete[]arr;

}

可见,它需要先进行递归,条件成立的话,继续递归,直到条件不成立,则返回,最后一层递归调用的函数返回之后才会执行下面归并的操作,所以要先将所有归并操作中要用到的参数都保存起来,最后再一层一层进行归并,保存参数的过程即为将所有的参数入栈,

而每次新入栈的参数决定于之前的参数,因为归并排序是分治解决问题的

while(head1<=head)head1表示用来决定新参数的参数在栈中的位置,head为新参数的///位置

       {

              x=sl[head1];

              y=sh[head1];

              head1++;

              m=(x+y)/2;

              if(x<m)

              {

                     head++;

                     sl[head]=x;

                     sh[head]=m;

              }

              if(m+1<y)

              {

                     head++;

                     sl[head]=m+1;

                     sh[head]=y;

              }

       }

栈建好之后,就可以在栈中从顶至底取参数,进行归并了

while(head>=0)

       {

              x=sl[head];

              y=sh[head];

              head--;

              m=(x+y)/2;

              int *arr=new int[y-x+1];

              i=x,j=m+1,k=0;

              while(i<=m&&j<=y)

              {

                     if(a[i]<a[j])

                     {

                            arr[k]=a[i];

                            i++;

                            k++;

                     }

                     else

                     {

                            arr[k]=a[j];

                            j++;

                            k++;

                     }

              }

              if(i<=m)

              {

                     while(i<=m)

                     {

                            arr[k]=a[i];

                            i++;k++;

                     }

              }

              if(j<=y)

              {

                     while(j<=y)

                     {

                            arr[k]=a[j];

                            k++;j++;

                     }

              }

              for(i=x;i<=y;i++)

              {

                     a[i]=arr[i-x];

              }

              delete[]arr;

       }

}

 

 

 

 

以上为个人学习总结, 欢迎reader们提出宝贵建议与指导~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值