递归是计算机科学中一个非常重要的概念,对于斐波那契那种比较简单的递归,分析起来比较容易,但是由于二叉树涉及指针操作,所以模仿下遍历过程中系统栈的情况。

以二叉树中序遍历为例演示:

//二叉树定义
struct TreeNode {
TreeNode* left;
TreeNode* right;
int val;
TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};

中序遍历的递归实现:



假设二叉树如图所示:



其中序遍历序列为\(2413\),可以在VS中用单步调试的方法跟踪相应的变量:

root==NULL(root指向2的左孩子)时,此时的系统栈(将1和2都压栈,因为中序遍历需要先访问左孩子):



这时if不成立,执行83行的return语句,接着退栈,回到78行,此时的root指向2(因为此时程序已经来到了新的栈顶),并且向这个新栈顶返回了一个空的seq



接着执行79行(因为这是上一个函数return的,所以不会再一次执行78行),将2存入seq中;

执行80行(root指向4),进而执行78行,root指向4的左孩子,此时的系统栈(很明显可以看到从栈底到栈顶依次存放根结点到当前root结点的路径上的结点):



同样,执行return语句,退栈,将seq(里面只有2)返回到这一层,这一层的root指向4,接着将4存入seq

到80行,调用inOrder()使得root指向4的右孩子,右孩子为空,所以返回并退栈,root重新指向4,此时80行执行完毕,整个if执行完毕,返回seq并退栈,root返回到了2,以2为根结点的子树中序遍历完毕,系统栈:



继续执行,return到78行,root指向1,将1存入seq,以此类推,就可以得到整个的遍历序列。

最关键的是:之所以要递归调用inOrder,就是因为现在还不想访问当前的结点(对于中序,要先找到最左边的结点),所以通过递归的方式将当前暂时不想访问的结点压入系统栈,找到了想访问的结点后,访问它并利用退栈操作返回父结点。

顺便记录一些经典的递归程序:

bool isPalindrome(string s) {
if (s.length() <= 1)
return true;
return s[0] == s[s.length() - 1] &&
isPalindrome(s.substr(1, s.length() - 2));
}
const int NotFound = -1;

int BSearch(vector<string>& v, int start, int stop, string key) {
if (start > stop) return NotFound; int mid = (start + stop) / 2;
if (key == v[mid])
return mid;
else if (key > v[mid])
return BSearch(v, mid + 1, stop, key);
else
return BSearch(v, start, mid - 1, key);
}
int C(int n, int k) {
if (n == k || k == 0)
return 1;
else
return C(n - 1, k) + C(n - 1, k - 1);
}
void permute(string soFar, string rest) {
if (rest == "")
cout << soFar << endl;
else {
for (int i = 0; i < rest.length(); ++i) {
string next = soFar + rest[i];
string remaining = rest.substr(0, i) + rest.substr(i + 1);
permute(next, remaining);
}
}
} // v2
void per(vector<int>& nums, int n, int d, vector<bool>& used, vector<int>& cur, vector<vector<int>>& ans) {
if (n == d) {
ans.push_back(cur);
return;
} for (int i = 0; i < nums.size(); ++i) {
if (used[i])
continue;
used[i] = true;
cur.push_back(nums[i]);
per(nums, n, d + 1, used, cur, ans);
cur.pop_back();
used[i] = false;
}
}
void com(vector<int>& nums, int n, int d, int start, vector<int>& cur, vector<vector<int>>& ans) {
if (n == d) {
ans.push_back(cur);
return;
} for (int i = start; i < nums.size(); ++i) {
cur.push_back(nums[i]);
com(nums, n, d + 1, i + 1, cur, ans);
cur.pop_back();
}
}
void subsets(string soFar,string rest) {
if (rest == "")
cout << soFar << endl;
else {
// add to subset, remove from rest, recur
subsets(soFar + rest[0], rest.substr(1));
// do not add to subset, remove from rest, recur
subsets(soFar, rest.substr(1));
}
}
// one root
func solve(root)
{
if(root == null) return ...
if f(root) return ... l = solve(root->left);
r = solve(root->right); return g(root, l , r);
}
// two roots
func solve(p, q)
{
if(p == null && q == null) return ...
if f(p, q) return ... l = solve(p.child, q.child);
r = solve(p.child, q.child); return g(p, q, l, r);
}

Recursion and System Stack的更多相关文章

  1. Data Structure Binary Tree: Inorder Tree Traversal without recursion and without stack!

    http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/ #include &l ...

  2. Security arrangements for extended USB protocol stack of a USB host system

    Security arrangements for a universal serial bus (USB) protocol stack of a USB host system are provi ...

  3. DSP中-stack和-heap的作用

    -stack           0x00000800-heap            0x00000800 stack - 又称系统栈(system stack),用于: 保存函数调用后的返回地址; ...

  4. [转]C/C++ 实现文件透明加解密

    今日遇见一个开超市的朋友,真没想到在高校开超市一个月可以达到月净利润50K,相比起我们程序员的工资,真是不可同日而语,这个世道啊,真是做程序员不如经商开超市, 我们高科技的从业者,真是造原子弹不如卖茶 ...

  5. Windows 错误代码

    Error Messages for Windows http://www.gregorybraun.com/MSWINERR.ZIP Server 4.0 Error Messages   Code ...

  6. Pintos-斯坦福大学操作系统Project详解-Project1

    转载请注明出处. 前言:  本实验来自斯坦福大学cs140课程,只限于教学用途,以下是他们对于Pintos系统的介绍:  Pintos is a simple operating system fra ...

  7. [转]Delphi I/O Errors

    The following are the Windows API (and former DOS) IO errors, which are also the IO errors often ret ...

  8. Live555 分析(三):客服端

    live555的客服端流程:建立任务计划对象--建立环境对象--处理用户输入的参数(RTSP地址)--创建RTSPClient实例--发出DESCRIBE--发出SETUP--发出PLAY--进入Lo ...

  9. 【算法】转载:Iterative vs. Recursive Approaches

    Iterative vs. Recursive Approaches Eyal Lantzman, 5 Nov 2007 CPOL             Introduction This arti ...

随机推荐

  1. Linux bash篇(二 操作环境)

    1.命令执行的顺序 (1).相对/绝对路径 (2).由alias找到的命令 (3).由bash内置的命令 (4).通过$PATH变量找到的第一个命令 2.第一篇讲到的bash在注销后就会无效,如果想保 ...

  2. PAT 链表倒序的算法优化

    之前的答案错误问题已经解决了,现在还有运行超时的问题,先贴上之前的代码 1 #include <iostream> 2 #include <string.h> 3 using ...

  3. 36 Thread 多线程

    /* * 多线程的实现方式: * 方式1:一种方法是将类声明为 Thread 的子类.该子类应重写 Thread 类的 run 方法.接下来可以分配并启动该子类的实例 * * Thread * Str ...

  4. 31.3 自定义异常类 MyException

    /* * 异常的分类: 运行时期异常:RuntimeException的子类就是运行时期异常,在编译时期可以自由选择处理或者不处理 编译时期异常:是Exception的子类,非RuntimeExcpe ...

  5. Anaconda下的juputer notebook 更改起始目录的方法【填坑】

    出来的结果是这样的,我们很不习惯,找文件.保存文件很麻烦 这里的快捷方式可以打开 jupyter notebook ,但是如果你没配置环境变量的话,在cmd 中 输入命令 jupyter notebo ...

  6. 关于连接内部服务器以及redis缓存基本操作

    1.linux命令行远程连接内部服务器 ssh -p 6637 mndevops@172.18.11.183 //建立连接命令 端口号和用户名+ip 输入密码 ./redis-cli 进入redis数 ...

  7. 使用Scanner接受用户键盘输入的值

    第一步导入Scanner import java.util.*; 第二步创建Scanner对象 Scanner input = new Scanner(System.in); 第二步创建Scanner ...

  8. 深入理解new运算符

    在 JavaScript 中,new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例.创建一个对象很简单,为什么我们还要多此一举使用 new 运算符呢?它到底有什么样的魔力? 认 ...

  9. 王者荣耀英雄全皮肤4K高清大图,python爬虫帮你保存下来

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取t.cn ...

  10. stand up meeting 11/19/2015

    队员 今日工作 工作耗时/h 明日计划 计划耗时/h 冯晓云 利用昨天编写的调用必应词典API的DLL,完成了UWP版本查词APP的试水,证实了DLL可调和在线查词的可行性:和其他部分的同学就接口数据 ...