将递归转化成迭代的通用技术

7 篇文章 0 订阅

转自:  http://blog.csdn.net/whinah/article/details/6419680


从理论上讲,只要允许使用栈,所有的递归程序都可以转化成迭代。

但是并非所有递归都必须用栈,不用堆栈也可以转化成迭代的,大致有两类

  1. 尾递归:可以通过简单的变换,让递归作为最后一条语句,并且仅此一个递归调用。

  1. // recursive  
  2. int fac1(int n) {  
  3.     if (n <= 0) return 1;  
  4.     return n * fac1(n-1);  
  5. }  
  6. // iterative  
  7. int fac2(int n) {  
  8.     int i = 0, y = 1;  
  9.     for (; i <= n; ++i) y *= i;  
  10.     return y;  
  11. }  
 

  1. 自顶向下->自底向上:对程序的结构有深刻理解后,自底向上计算,比如 fibnacci 数列的递归->迭代转化。

  1. // recursive, top-down  
  2. int fib1(int n) {  
  3.     if (n <= 1) return 1;  
  4.     return fib1(n-1) + fib1(n-2);  
  5. }  
  6. // iterative, down-top  
  7. int fib2(int n) {  
  8.     int f0 = 1, f1 = 1, i;  
  9.     for (i = 2; i <= n; ++i) {  
  10.         int f2 = f1 + f0;  
  11.         f0 = f1; f1 = f2;  
  12.     }  
  13.     return f1;  
  14. }  
 

 

对于非尾递归,就必须使用堆栈。可以简单生硬地使用堆栈进行转化:把函数调用和返回的地方翻译成汇编代码,然后把对硬件 stack 的  push, pop 操作转化成对私有 stack 的 push, pop ,这其中需要特别注意的是对返回地址的 push/pop,对应的硬件指令一般是 call/ret。使用私有 stack 有两个好处:

  1. 可以省去公用局部变量,也就是在任何一次递归调用中都完全相同的函数参数,再加上从这些参数计算出来的局部变量。
  2. 如果需要得到当前的递归深度,可以从私有 stack 直接拿到,而用递归一般需要一个单独的 depth 变量,然后每次递归调用加 1。

我们把私有 stack 元素称为 Frame,那么 Frame 中必须包含以下信息:

  1. 返回地址(对应于每个递归调用的下一条语句的地址)
  2. 对每次递归调用都不同的参数

通过实际操作,我发现,有一类递归的 Frame 可以省去返回地址!所以,这里又分为两种情况:

  • Frame 中可以省去返回地址的递归:仅有两个递归调用,并且其中有一个是尾递归。

    1. // here used a function 'partition', but don't implement it  
    2. tempalte<class RandIter>  
    3. void QuickSort1(RandIter beg, RandIter end) {  
    4.     if (end - beg <= 1) return;  
    5.     RandIter pos = partition(beg, end);  
    6.     QuickSort1(beg, pos);  
    7.     QuickSort1(pos + 1, end);  
    8. }  
    9. tempalte<class RandIter>  
    10. void QuickSort2(RandIter beg, RandIter end) {  
    11.     std::stack<std::pair<RandIter> > stk;  
    12.     stk.push({beg, end});  
    13.     while (!stk.empty()) {  
    14.         std::pair<RandIter, RandIter> ii = stk.top(); stk.pop();  
    15.         if (ii.second - ii.first) > 1) {  
    16.             RandIter pos = partition(beg, end);  
    17.             stk.push({ii.first, pos});  
    18.             stk.push({pos + 1, ii.second});  
    19.         }  
    20.     }  
    21. }  
     

    • Frame 中必须包含返回地址的递归,这个比较复杂,所以我写了个完整的示例:
      • 以MergeSort为例,因为 MergeSort 是个后序过程,两个递归调用中没有任何一个是尾递归
      • MergeSort3 使用了 GCC 的 Label As Value 特性,只能在 GCC 兼容的编译器中使用
      • 单纯对于这个实例来说,返回地址其实只有两种,返回地址为 0 的情况可以通过判断私有栈(varname=stk)是否为空,stk为空时等效于 retaddr == 0。如果要精益求精,一般情况下指针的最低位总是0,可以把这个标志保存在指针的最低位,当然,如此的话就无法对 sizeof(T)==1 的对象如 char 进行排序了。
        1. #include <stdio.h>  
        2. #include <string.h>  
        3. # if 1  
        4. #include <stack>  
        5. #include <vector>  
        6. template<class T>  
        7. class MyStack : public std::stack<T, std::vector<T> >  
        8. {  
        9. };  
        10. #else  
        11. template<class T>  
        12. class MyStack {  
        13.     union {  
        14.         char*  a;  
        15.         T* p;  
        16.     };  
        17.     int n, t;  
        18. public:  
        19.     explicit MyStack(int n=128) {  
        20.         this->n = n;  
        21.         this->t = 0;  
        22.         a = new char[n*sizeof(T)];  
        23.     }  
        24.     ~MyStack() {  
        25.         while (t > 0)  
        26.             pop();  
        27.         delete[] a;  
        28.     }  
        29.     void swap(MyStack<T>& y) {  
        30.         char* q = y.a; y.a = a; a = q;  
        31.         int z;  
        32.         z = y.n; y.n = n; n = z;  
        33.         z = y.t; y.t = t; t = z;  
        34.     }  
        35.     T& top() const {   
        36.         return p[t-1];  
        37.     }  
        38.     void pop() {  
        39.         --t;  
        40.         p[t].~T();  
        41.     }  
        42.     void push(const T& x) {  
        43.         x.print(); // debug  
        44.         p[t] = x;  
        45.         ++t;  
        46.     }  
        47.     int size() const { return t; }  
        48.     bool empty() const { return 0 == t; }  
        49.     bool full() const { return n == t; }  
        50. };  
        51. #endif  
        52. template<class T>  
        53. struct Frame {  
        54.     static T* base;  
        55.     T *beg, *tmp;  
        56.     int len;  
        57.     int retaddr;  
        58.     Frame(T* beg, T* tmp, int len, int retaddr)  
        59.         : beg(beg), tmp(tmp), len(len), retaddr(retaddr)  
        60.     {}  
        61.     void print() const { // for debug  
        62.         printf("%4d %4d %d/n"int(beg-base), len, retaddr);  
        63.     }  
        64. };  
        65. template<class T> T* Frame<T>::base;  
        66. #define TOP(field) stk.top().field  
        67. template<class T>  
        68. bool issorted(const T* a, int n)  
        69. {  
        70.     for (int i = 1; i < n; ++i) {  
        71.         if (a[i-1] > a[i]) return false;  
        72.     }  
        73.     return true;  
        74. }  
        75. template<class T>  
        76. void mymerge(const T* a, int la, const T* b, int lb, T* c) {  
        77.     int i = 0, j = 0, k = 0;  
        78.     for (; i < la && j < lb; ++k) {  
        79.         if (b[j] < a[i])  
        80.             c[k] = b[j], ++j;  
        81.         else  
        82.             c[k] = a[i], ++i;  
        83.     }  
        84.     for (; i < la; ++i, ++k) c[k] = a[i];  
        85.     for (; j < lb; ++j, ++k) c[k] = b[j];  
        86. }  
        87. template<class T>  
        88. void MergeSort1(T* beg, T* tmp, int len) {  
        89.     if (len > 1) {  
        90.         int mid = len / 2;  
        91.         MergeSort1(beg    , tmp    , mid);  
        92.         MergeSort1(beg+mid, tmp+mid, len-mid);  
        93.         mymerge(tmp, mid, tmp+mid, len-mid, beg);  
        94.         memcpy(tmp, beg, sizeof(T)*len);  
        95.     }  
        96.     else  
        97.         *tmp = *beg;  
        98. }  
        99. template<class T>  
        100. void MergeSort2(T* beg0, T* tmp0, int len0) {  
        101.     int mid;  
        102.     int cnt = 0;  
        103.     Frame<T>::base = beg0;  
        104.     MyStack<Frame<T> > stk;  
        105.     stk.push(Frame<T>(beg0, tmp0, len0, 0));  
        106.     while (true) {  
        107.         ++cnt;  
        108.         if (TOP(len) > 1) {  
        109.             mid = TOP(len) / 2;  
        110.             stk.push(Frame<T>(TOP(beg), TOP(tmp), mid, 1));  
        111.             continue;  
        112. L1:  
        113.             mid = TOP(len) / 2;  
        114.             stk.push(Frame<T>(TOP(beg)+mid, TOP(tmp)+mid, TOP(len)-mid, 2));  
        115.             continue;  
        116. L2:  
        117.             mid = TOP(len) / 2;  
        118.             mymerge(TOP(tmp), mid, TOP(tmp)+mid, TOP(len)-mid, TOP(beg));  
        119.             memcpy(TOP(tmp), TOP(beg), sizeof(T)*TOP(len));  
        120.         } else  
        121.             *TOP(tmp) = *TOP(beg);   
        122.         int retaddr0 = TOP(retaddr);  
        123.         stk.pop();  
        124.         switch (retaddr0) {  
        125.         case 0: return;  
        126.         case 1: goto L1;  
        127.         case 2: goto L2;  
        128.         }  
        129.     }  
        130. }  
        131. // This Implementation Use GCC's goto saved label value  
        132. // Very similiar with recursive version  
        133. template<class T>  
        134. void MergeSort3(T* beg0, T* tmp0, int len0) {  
        135. MyEntry:  
        136.     int mid;  
        137.     int retaddr;  
        138.     Frame<T>::base = beg0;  
        139.     MyStack<Frame<T> > stk;  
        140.     stk.push(Frame<T>(beg0, tmp0, len0, 0));  
        141. #define Cat1(a,b) a##b  
        142. #define Cat(a,b) Cat1(a,b)  
        143. #define HereLabel() Cat(HereLable_, __LINE__)  
        144. #define RecursiveCall(beg, tmp, len) /  
        145.     stk.push(Frame<T>(beg, tmp, len, (char*)&&HereLabel() - (char*)&&MyEntry)); /  
        146.     continue; /  
        147.     HereLabel():;  
        148. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
        149. // retaddr == 0 是最外层的递归调用,  
        150. // 只要到达这一层时 retaddr 才为 0,  
        151. // 此时就可以返回了  
        152. #define MyReturn /  
        153.     retaddr = TOP(retaddr); /  
        154.     stk.pop(); /  
        155.     if (0 == retaddr) { /  
        156.         return; /  
        157.     } /  
        158.     goto *((char*)&&MyEntry + retaddr);  
        159. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
        160.     while (true) {  
        161.         if (TOP(len) > 1) {  
        162.             mid = TOP(len) / 2;  
        163.             RecursiveCall(TOP(beg), TOP(tmp), mid);  
        164.             mid = TOP(len) / 2;  
        165.             RecursiveCall(TOP(beg)+mid, TOP(tmp)+mid, TOP(len)-mid);  
        166.             mid = TOP(len) / 2;  
        167.             mymerge(TOP(tmp), mid, TOP(tmp)+mid, TOP(len)-mid, TOP(beg));  
        168.             memcpy(TOP(tmp), TOP(beg), sizeof(T)*TOP(len));  
        169.         } else  
        170.             *TOP(tmp) = *TOP(beg);   
        171.         MyReturn;  
        172.     }  
        173. }  
        174. template<class T>  
        175. void MergeSortDriver(T* beg, int len, void (*mf)(T* beg_, T* tmp_, int len_))  
        176. {  
        177.     T* tmp = new T[len];  
        178.     (*mf)(beg, tmp, len);  
        179.     delete[] tmp;  
        180. }  
        181. #define test(a,n,mf) /  
        182.     memcpy(a, b, sizeof(a[0])*n); /  
        183.     MergeSortDriver(a, n, &mf); /  
        184.     printf("sort by %s:", #mf); /  
        185.     for (i = 0; i < n; ++i) printf("% ld", a[i]); /  
        186.     printf("/n");  
        187. int main(int argc, char* argv[])  
        188. {  
        189.     int n = argc - 1;  
        190.     int i;  
        191.     long* a = new long[n];  
        192.     long* b = new long[n];  
        193.     for (i = 0; i < n; ++i)  
        194.         b[i] = strtol(argv[i+1], NULL, 10);  
        195.     test(a, n, MergeSort1);  
        196.     test(a, n, MergeSort2);  
        197.     test(a, n, MergeSort3);  
        198.     printf("All Successed/n");  
        199.     delete[] a;  
        200.     delete[] b;  
        201.     return 0;  
        202. }  
         
    • 1
      点赞
    • 2
      收藏
      觉得还不错? 一键收藏
    • 1
      评论

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

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

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值