手写栈(递归转化为非递归)

递归的本质是通过栈来保存状态,然后再次调用自己进入新的状态,然后函数返回的时候回到上次保存的状态。

如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,就是没有回溯过程,所以我们可以直接将尾递归写成循环

更一般的递归,想要转化为非递归,就需要模拟栈(手写栈)的行为。

遍历的递归和非递归实现:

#include<cstdio>
#include<stack>
using namespace std;
struct node{
    int id;
    node *l=NULL,*r=NULL;
}a[100];
void PreOrderTraverse(node i){//递归先序遍历
    printf("%d ",i.id);//输出根
    if(i.l)PreOrderTraverse(*i.l);//遍历左子树
    if(i.r)PreOrderTraverse(*i.r);//遍历右子树
}
void InOrderTraverse(node i){//递归中序遍历
    if(i.l)InOrderTraverse(*i.l);//遍历左子树
    printf("%d ",i.id);//输出根
    if(i.r)InOrderTraverse(*i.r);//遍历右子树
}
void PostOrderTraverse(node i){//递归后序遍历
    if(i.l)PostOrderTraverse(*i.l);//遍历左子树
    if(i.r)PostOrderTraverse(*i.r);//遍历右子树
    printf("%d ",i.id);//输出根
}
void PreOrderTraverse(node* i){//非递归先序遍历
    stack<node>s;
    node x;
    while(i||!s.empty()){
        while(i){
            s.push(*i);
            printf("%d ",i->id);//输出根
            i=i->l;//遍历左子树
        }
        x=s.top();s.pop();
        i=x.r;//遍历右子树

    }
}
void InOrderTraverse(node* i){//非递归中序遍历
    stack<node>s;
    node x;
    while(i||!s.empty()){
        while(i){
            s.push(*i);
            i=i->l;//遍历左子树
        }
        x=s.top();s.pop();
        printf("%d ",x.id);//输出根
        i=x.r;//遍历右子树

    }
}
void PostOrderTraverse(node* i){//非递归后序遍历
    stack<pair<node,bool> >s;
    pair<node,bool> x;
    while(i||!s.empty()){
        while(i){
            s.push(make_pair(*i,0));
            i=i->l;//遍历左子树
        }
        x=s.top();s.pop();
        if(x.second)printf("%d ",x.first.id);//如果右子树遍历过则输出根
        else s.push(make_pair(x.first,1)),i=x.first.r;//如果右子树没遍历则遍历右子树

    }
}
int main(){
    for(int i=1;i<99;i++){
        a[i].id=i;
        if(i%2)a[i>>1].r=&a[i];
        else a[i>>1].l=&a[i];
    }
    InOrderTraverse(a[1]);printf("\n");
    InOrderTraverse(&a[1]);printf("\n");
    PreOrderTraverse(a[1]);printf("\n");
    PreOrderTraverse(&a[1]);printf("\n");
    PostOrderTraverse(a[1]);printf("\n");
    PostOrderTraverse(&a[1]);printf("\n");
    return 0;
}
时间: 2024-10-05 22:01:15

手写栈(递归转化为非递归)的相关文章

dfs手写栈模板

在竞赛中如果系统栈很小的话,过深的递归会让栈溢出,这个时候我们就要自己手写栈,将递归转化成手工栈. 方法其实也很简单. 基本思路上,我们就是用栈不断的pop,push.但是何时push,何时pop呢? 在<算法导论>上对深度优先遍历树的讲解中,在深度遍历中,会对每个节点进行染色,白色为没有被访问过:灰色为被访问过,但是该节点的所有子树还没有完成访问:黑色,节点被访问过,而且该节点的所有子树都被完全的访问. 所以,我们就通过颜色标记来进行判断了. 整体的框架如下: memset(vis,0,si

递归如何转换为非递归

递归算法实际上是一种分而治之的方法,它把复杂问题分解为简单问题来求解.递归的特点包括:递归过程简洁.易编.易懂:递归过程效率低.重复计算多. 考虑递归的执行效率低,可以尝试将递归过程转换为非递归过程.本文就是来探讨怎么转换的. 将递归算法转换为非递归算法有两种方法,一种是直接求值(迭代/循环),不需要回溯:另一种是不能直接求值,需要回溯.前者使用一些变量保存中间结果,称为直接转换法:后者使用栈保存中间结果,称为间接转换法,下面分别讨论这两种方法. 一.直接转换法 直接转换法通常用来消除尾递归和单

数据结构二叉树——建立二叉树、中序递归遍历、非递归遍历、层次遍历

数据结构二叉树-- 编写函数实现:建立二叉树.中序递归遍历.借助栈实现中序非递归遍历.借助队列实现层次遍历.求高度.结点数.叶子数及交换左右子树. ("."表示空子树) #include<stdio.h> #include<stdlib.h> //***********二叉树链表节点结构 typedef char DataType; typedef struct Node {  DataType data;  struct Node*LChild;  struc

二叉树的递归遍历和非递归遍历(附详细例子)

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump

POJ 1780 Code 欧拉回路+手写栈DFS

和西安邀请赛那道题题目差不多,现在终于会手写栈了,自己琢磨了好久,真是感动TAT #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdli

【C语言】求斐波那契(Fibonacci)数列通项(递归法、非递归法)

意大利的数学家列昂那多·斐波那契在1202年研究兔子产崽问题时发现了此数列.设一对大兔子每月生一对小兔子,每对新生兔在出生一个月后又下崽,假若兔子都不死亡.   问:一对兔子,一年能繁殖成多少对兔子?题中本质上有两类兔子:一类是能生殖的兔子,简称为大兔子:新生的兔子不能生殖,简称为小兔子:小兔子一个月就长成大兔子.求的是大兔子与小兔子的总和. 月     份  ⅠⅡ  Ⅲ  Ⅳ  Ⅴ Ⅵ  Ⅶ  Ⅷ Ⅸ Ⅹ  Ⅺ  Ⅻ大兔对数 1  1   2   3   5  8  13  21 34 55 

3.4.4 利用栈将递归转换成非递归的方法

在函数执行时系统需要设立一个“递归工作栈”存储第一层递归所需的信息,此工作栈是递归函数执行的辅助空间,所以可以看出,递归程序在执行时需要系统提供隐式栈这种数据结构来实现,对于一般的递归过程,仿照递归算法执行过程中递归工作栈的状态变化可直接写出相应的非递归算法.这种利用栈消除递归过程的步骤如下. (1)设置一个工作栈存放递归工作记录(包括实参.返回地址及局部变量等) (2)进入非递归调用入口(即被调用程序开始处)将调用程序传来的实在参数和返回地址入栈(递归程序不可以作为主程序,因而可认为初始是被某

二叉树的遍历--递归实现与非递归实现

二叉树的表示 在研究二叉树的遍历之前,我们需要先看看二叉树的表示方式. 一般来说,我们使用自定义的数据结构或是数组来表示二叉树. 二叉树的数据结构: public class TreeNode { public int val; // 左孩子 public TreeNode left; // 右孩子 public TreeNode right; } 数组形式表现二叉树 当我们使用数组形式表现二叉树时,我们将数组第一个节点的索引置为「1」,也就是根节点,如果我们通用性的将其当为「x」,那么它的左孩

数据结构--Avl树的创建,插入的递归版本和非递归版本,删除等操作

AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个结点的左右子树的高度之差的绝对值最多为1(空树的高度为-1). 也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树). 对Avl树进行相关的操作最重要的是要保持Avl树的平衡条件.即对Avl树进行相关的操作后,要进行相应的旋转操作来恢复Avl树的平衡条件. 对Avl树的插入和删除都可以用递归实现,文中也给出了插入的非递归版本,关键在于要用到栈. 代码如下: #inclu