博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
算法笔记 - 用栈模拟递归实现二叉树遍历
阅读量:6237 次
发布时间:2019-06-22

本文共 4240 字,大约阅读时间需要 14 分钟。

二叉树

这次是一个二叉树的算法。我首先列出二叉树类的代码清单:

template 
class tree;template
class node { friend class tree
;public: typedef T value_type;protected: node* _l = nullptr; node* _r = nullptr; value_type _v;public: const node& left() const { if(!_l) throw std::runtime_error("Null Pointer"); return *_l; } const node& right() const { if(!_r) throw std::runtime_error("Null Pointer"); return *_r; } node& left() { if(!_l) _l = new node(); return *_l; } node& left(const value_type& v) { if(!_l) _l = new node(v); else _l->_v = v; return *this; } bool hasl() const { return _l; } bool hasr() const { return _r; } node& right() { if(!_r) _r = new node(); return *_r; } node& right(const value_type& v) { if(!_r) _r = new node(v); else _r->_v = v; return *this; } node() { } node(const value_type& v) : _v(v) { } ~node() { if(_l) delete _l; if(_r) delete _r; } const value_type& val() const { return _v; } value_type& val() { return _v; } node& val(const value_type& v) { _v = v; return *this; }};template
class tree {public: typedef T value_type; typedef node
node_type;protected: node_type _root;public: tree() { } tree(const value_type& v) : _root(v) { } node_type& root() { return _root; } const node_type& root() const { return _root; } node_type& root(const value_type& v) { _root._v = v; return _root; }};int main(){ tree
stree; stree.root("A").left("B") .left().left("C").right("D") .right().left("E").right("F") .left().right("G");}

main 中所建造的二叉树有这样的结构:

A     /    B   / \  C   D     / \    E   F     \      G

递归先根遍历

不用脑想就随手打出来三行代码:

template 
void traverse(node
const & t){ cout << t.val() << endl; if(t.hasl()) traverse(t.left()); if(t.hasr()) traverse(t.right());}

它的输出结果是:

ABCDEGF

用栈来模拟递归遍历

大家知道“栈”这个东西,在操作系统概念或者x86汇编的概念上也有一个,是ESP到EBP所划分的一段内存。在这个栈上面保存着现场(Scenery),所谓现场,包含两样东西:一是局部变量,二是程序返回的位置。所以在这里,我们用一个pair<node*, int>来表示现场。每一次循环,从栈顶恢复局部变量loc.first和返回位置loc.second

template 
void traverse_non_recursive_1(node
const & _t){ typedef pair
const *, int> scenery; stack
s; s.push(scenery(&_t, 0)); while(!s.empty()) { scenery& loc = s.top(); node
const & t = *loc.first; switch(loc.second++) { case 0: cout << t.val() << endl; if(t.hasl()) s.push(make_pair(&t.left(), 0)); break; case 1: if(t.hasr()) s.push(make_pair(&t.right(), 0)); break; case 2: s.pop(); break; } }}

尾递归优化

右子树的递归if(t.hasr()) traverse(t.right()),因为在其后已经没有任何语句了,所以属于尾递归。在尾递归发生的情况下,对当前局部变量的修改不会对代码执行产生任何影响(因为已经没有什么可执行的代码了)。所以,我们直接覆盖栈顶,而不是将下一次调用所需的变量和返回位置压栈。这样可以节省内存空间,并且节约代码:

template 
void traverse_non_recursive_2(node
const & _t){ typedef pair
const *, int> scenery; stack
s; s.push(scenery(&_t, 0)); while(!s.empty()) { scenery& loc = s.top(); node
const & t = *loc.first; switch(loc.second++) { case 0: cout << t.val() << endl; if(t.hasl()) s.push(make_pair(&t.left(), 0)); break; case 1: if(t.hasr()) s.top() = make_pair(&t.right(), 0); else s.pop(); break; } }}

0-1优化

0-1优化是我创造的名词(笑),因为这种优化方案在只有两个代码段(或者说只发生一次递归,或者第二次递归为尾递归而被优化了)的时候可以适用。如果我们在执行途中打印出栈内所有element.second(返回位置),我们只可能见到两种情况:

1 1 1 1 ... 1 1 1 01 1 1 1 ... 1 1

也就是说,除开栈顶元素,其余元素只可能为1。原因是:在将新元素压栈之前,当前栈顶必然已经不为0(loc.second++处)。所以,我们只需记录最后一个元素即可。这里用bt(backtrace)标记栈顶是否是回溯回来的而不是新压的:

template 
void traverse_non_recursive_3(node
const & _t){ stack
const *> s; s.push(&_t); bool bt = false; while(!s.empty()) { node
const & t = *s.top(); if(!bt) { cout << t.val() << endl; if(t.hasl()) { bt = false; s.push(&t.left()); } else bt = true; } else { if(t.hasr()) { bt = false; s.top() = &t.right(); } else { s.pop(); bt = true; } } }}

转载地址:http://aykia.baihongyu.com/

你可能感兴趣的文章
从无线安全到内网渗透
查看>>
Xamarin iOS教程之申请付费开发者账号下载证书
查看>>
!+"\v1" 用来“判断浏览器类型”还是用来“IE判断版本”的问题!
查看>>
javascript之Style物
查看>>
C# 公历转农历
查看>>
LeetCode - Divide Two Integers
查看>>
去掉 “当前安全设置会使计算机有风险”提示
查看>>
sql 聚合函数
查看>>
ABP源码分析二十:ApplicationService
查看>>
学习OpenCV——BOW特征提取函数(特征点篇)
查看>>
帮你店铺日销千单的20个淘宝小技巧
查看>>
python deep copy and shallow copy
查看>>
I.MX6 Ethernet MAC (ENET) MAC Address hacking
查看>>
下载本 WebEnh博客 安卓APP
查看>>
iOS中常见 Crash 及解决方案
查看>>
【python】datetime获取日期,前一天日期
查看>>
Lua简易入门教程
查看>>
如果使用百度云盘同步电脑里文件夹
查看>>
linux内核栈与用户栈【转】
查看>>
一次完整的http事务
查看>>