肚子为什么会疼| 头痛头晕吃什么药| 胃痉挛吃什么药最有效| 早泄是什么原因| 拿手机手抖是什么原因| 汤力水是什么| 左胸隐隐作痛是什么原因| 古驰属于什么档次| 追剧是什么意思| 五七是什么意思有什么讲究| 传字五行属什么| 布洛芬不能和什么药一起吃| 鼻基底用什么填充最好| 左肋骨下方隐隐疼痛是什么原因| 贴士是什么意思| 什么是中暑| 现充什么意思| 什么伤肝| 蚊子为什么吸血| 清谷天指的是什么| 肺炎可以吃什么水果| 子宫破裂有什么危险| 脚麻是什么原因造成的| 什么长而去| 被蜜蜂蛰了涂什么药膏| 吹气检查胃是检查什么| 条子是什么意思| 三手烟是什么意思| 银离子是什么| 肌肉的作用是什么| 老来得子是什么意思| 6月7日是什么星座| 什么姿势睡觉最好| 接济是什么意思| 风寒吃什么感冒药| 鸡炖什么好吃又有营养| 精液是什么组成的| 头晕做什么检查最准确| 迂回战术什么意思| 白细胞高是什么原因| bni是什么意思| 酚咖片是什么药| 奎宁现在叫什么药| 最近老坏东西暗示什么| 气溶胶传播是什么意思| ahc是什么牌子| 心有余悸是什么意思| 胃胀反酸吃什么药效果好| 傲娇什么意思| 舌头口腔溃疡是什么原因引起的| 四月八日是什么星座| 咳嗽黄痰是什么原因| 痔疮和肛周脓肿有什么区别| 朱雀玄武是什么意思| 肝火旺吃什么调理| 肌肉一跳一跳什么原因| 晚上失眠是什么原因| 张卫健属什么生肖| 没什么大不了的| 真露兑什么好喝| 过期红酒有什么用途| 闭关修炼是什么意思| 世界上最大的哺乳动物是什么| 肺结核吃什么好| 什么叫便秘| 人死后为什么要守夜| 夏天喝绿茶有什么好处| 人中长痘是什么原因| 捕风捉影是什么意思| 急性荨麻疹用什么药| 老酒是什么酒| 成人男性尿床是什么原因造成的| 吃中药能吃什么水果| 乙型肝炎e抗体阳性是什么意思| 鸡珍是什么| 缅怀是什么意思| 猪八戒是什么佛| 松花蛋是什么蛋做的| 猫咪发烧吃什么药| 上环要做什么检查| 腿抽筋用什么药| 胃酸多吃什么药| 医保断了一个月有什么影响| 小本创业做什么生意好| 红烧肉炖什么菜最好吃| 上相是什么意思| 颐养天年是什么意思| 尿毒症是什么病| 猪巴皮是什么材质| 雀斑是什么原因引起的| 九寨沟属于什么市| 免疫力低下吃什么| 小孩腰疼是什么原因| 温度计里面红色液体是什么| 控告是什么意思| 谈情说爱是什么意思| 子宫憩室是什么意思| 胆水的成分是什么| 吃刺猬有什么好处| 宰相是现在的什么官| 感冒咳嗽可以吃什么水果| 肾结石吃什么好| 扁桃体溃疡吃什么药| 包皮过长会有什么影响| 吃什么回奶最快最有效| 7.23什么星座| 胆结石吃什么排石最快| 大脑供血不足用什么药| 霉菌性中耳炎用什么药| 偏光太阳镜是什么意思| 天蝎座和什么星座最不配| 什么是伤官见官| 七月五日是什么星座| 婴儿大便隐血阳性是什么意思| 石蜡是什么东西| 回不到我们的从前是什么歌| 退而求其次是什么意思| 孤寡老人是什么意思| 夏至为什么吃馄饨| 九五年属什么生肖| 什么是健康证| 为什么会得抑郁症| 什么病不能吃鸡蛋| qq是什么| 幽闭是什么意思| 胃不消化吃什么药好| 更年期是什么| 卡粉是什么原因引起的| touch是什么牌子| 广州有什么小吃特产| 余事勿取什么意思| 军长相当于地方什么官| 消停是什么意思| 甲状腺结节吃什么| nec投影仪是什么牌子| 捷字五行属什么| 手指发红是什么原因| 迪卡侬属于什么档次| 两面人是什么意思| 前列腺肥大是什么症状| 怀孕了不想要最好的办法是什么| 牙根疼是什么原因| 维生素e有什么用| 123是什么意思| 下体瘙痒用什么药| 万力什么字| 更年期挂什么科| 坐月子能吃什么蔬菜| 厥阴病是什么意思| 阴液是什么| 什么级别可以配秘书| 沅字五行属什么| 甲状腺偏高是什么原因引起的| 兔肉不能和什么一起吃| 省内流量是什么意思| 肥皂水是什么| 胸口疼痛什么原因| 声讨是什么意思| 病案号是什么意思| 梧桐树的叶子像什么| 老梗是什么病| 狗狗的鼻子为什么是湿的| 七夕节是什么意思| 表现是什么意思| 小老头是什么意思| 头尖适合什么发型| 轶是什么意思| 草缸适合养什么鱼| 什么是好朋友| 1975年属兔是什么命| 定妆喷雾什么时候用| 情绪低落是什么意思| 香奈儿是什么牌子| 呼吸困难是什么原因引起的| 印堂发亮预兆着什么| 2011属什么生肖| 舌头有点麻是什么病的前兆| 这叫什么| 过敏吃什么药最有效| 梦见摘桑葚是什么意思| 乌鸦兄弟告诉我们什么道理| pnc是什么意思| 遇到黄鼠狼是什么征兆| 经由是什么意思| ube手术是什么意思| 鸡蛋花的花语是什么| 什么叫子宫腺肌症| 9月3号什么日子| 一语惊醒梦中人是什么意思| 祸祸是什么意思| 晚上失眠是什么原因| 以身相许是什么意思| 坐立不安是什么意思| 痛风买什么药| 沧州有什么好玩的地方| 甲肝阳性是什么意思| 一什么河| 皮肤脱皮是什么原因| 水洗真丝是什么面料| 左撇子是什么意思| 头出汗多是什么原因| 胎盘厚有什么影响| 益生菌吃了有什么好处| 为什么邓超对鹿晗很好| 孩子满月送什么礼物| 降甘油三酯吃什么食物最好| 为什么同房会有刺痛感| 嘴苦嘴臭什么原因| 三七甘一是什么意思| 真丝衣服用什么洗最好| 雪对什么| 刺五加配什么药治失眠| 霆字五行属什么| 阴间是什么意思| 回肠荡气什么意思| 肺上有结节是什么意思| 原发性肝ca什么意思| 为什么会莫名其妙的哭| 减肥晚餐适合吃什么| 更年期出虚汗吃什么药| 下载什么软件可以赚钱| 感冒流鼻涕吃什么药好得快| 性是什么| 早上起来眼皮肿是什么原因| 发改委主任什么级别| 胃反酸吃什么药最好| 雷蒙欣氨麻美敏片是什么药| 屁股下垂穿什么裤子| 老鹰的天敌是什么| 胆固醇过高有什么危害| 补牙用什么材料最好| 什么拉车连蹦带跳| 帝舵手表什么档次| 1993年属鸡是什么命| 横行霸道的意思是什么| 散片是什么意思| 风湿性关节炎吃什么药| ipf是什么病| 泪点低什么意思| 阴囊潮湿吃什么药| 文字属于五行属什么| 眼睛流泪用什么药| 尿蛋白什么意思| 猕猴桃是什么季节的水果| 什么拼音怎么写| 什么火锅最好吃| pco是什么意思| 身体出现白斑有可能患什么病| 梦房子倒塌什么预兆| 大胯疼是什么原因引起| 胶体金法是什么意思| 包二奶是什么意思| 第一次见女方家长带什么礼物好| 2014属什么生肖| 什么眼霜比较好用| 胸口正中间疼痛是什么病症| 宝宝手脚冰凉是什么原因| 群星是什么意思| 西湖龙井属于什么茶| 牙龈疼吃什么药| 办理无犯罪记录证明需要什么材料| 什么是性质| 女人脚肿是什么原因| 枭雄的意思是什么| 百度
Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Tree traversal without recursion: the tree as a state machine

by Aristotle (Chancellor)
on Feb 16, 2007 at 15:22 UTC ( [id://600456]=perlmeditation: print w/replies, xml ) Need Help??

I am currently working my way through Higher-Order Perl, and as you抎 expect, the subject of tree traversal makes a frequent apperance:

  1. First, as an introductory example to recursion;
  2. then, when discussing how to turn recursive functions into iterators using an explicit stack (which permits breadth-first searching);
  3. again recursively, in the section on tail call elimination, where the tail-recursive call is eliminated first, and the other recursive call is then replaced by an explicit stack.

There may be even more appearances later in the book that I抳e yet to discover; as I said, I抦 not through with it yet. However, the book changes topic after that, at least momentarily, so I stopped to ponder. It occured to me that this is the entire extent to which discussions of tree traversal typically go. Another obvious option that occured to me many years ago is not discussed anywhere that I抳e seen, though it is occasionally mentioned as a possibility in passing:

You can get rid of any stacks whatsoever by keeping a parent pointer in the tree node data structure. Effectively, this turns the tree into a (sort of) state machine. While traversing, you need no memory other than the current and the previous node/state. The traversal algorithm is very simple:

  1. If the previous node is this node抯 parent node, descend to the left child node.
  2. If the previous node is this node抯 left child node, descend to the right child node.
  3. If the previous node is this node抯 right child node, ascend to the parent node.

Obviously, if there is no left child to descend to, you try the right one; and if there is no right child to descend to, you ascend to the parent. Traversal is complete when an attempt to ascend to the parent node fails because there is no parent. Pre-, post- and in-order traversal can be implemented simply by changing which of the conditions implies that the current node must be visited: if you visit the node when coming from?/p>

  1. ?the parent node, you get pre-order traversal.
  2. ?the left child node; you get in-order traversal.
  3. ?the right child node; you get post-order traversal.

Assuming all tree nodes are instances of a class which has parent, left and right methods and uses undef to signify the absence of a pointer, the following is an implementation of the in-order version of the traversal algorithm in Perl:

sub traverse_tree { my ( $tree_root, $visitor_callback ) = @_; my ( $curr_node, $prev_node ) = $tree_root; while( $curr_node ) { my $next_node; if( $prev_node == $curr_node->parent ) { $next_node = $curr_node->left; if( not $next_node ) { $visitor_callback->( $curr_node ); $next_node = $curr_node->right || $curr_node->parent; } } elsif( $prev_node == $curr_node->left ) { $visitor_callback->( $curr_node ); $next_node = $curr_node->right || $curr_node->parent; } elsif( $prev_node == $curr_node->right ) { $next_node = $curr_node->parent; } ( $prev_node, $curr_node ) = ( $curr_node, $next_node ); } }

This is the most straightforward implementation, which does have a fault: there is some code duplication between the coming-from-parent and coming-from-left-child states. The complication comes about because node visiting must be ensured even when the node does not have the particular pointer to come from; eg. in the case of in-order traversal, you visit the current node when you come from the left child node; but when a node has no left child node, you must still ensure that the node will be visited. The discovery that the left child node is absent will happen when the previous node was the parent, and so that state must ensure to visit the current node before going on to try to descend to the right.

The fix is conceptually simple, but not easy to express in code. You need a way to fall through from the body of one branch to another抯 without checking the condition for that branch, much the way C抯 switch statement works, where branches fall through by default and require an explicit break to exit. A switch statement in C is simply a structured expression of a jump table (but note that you couldn抰 actually use a switch statement in C for this because the case conditions in this algorithm wouldn抰 be constant expressions); so the Perl version will need a couple of explicit gotos:

sub traverse_tree { my ( $tree_root, $visitor_callback ) = @_; my ( $curr_node, $prev_node ) = $tree_root; while( $curr_node ) { my $next_node; { goto FROM_PARENT if $prev_node == $curr_node->parent; goto FROM_LEFT if $prev_node == $curr_node->left; goto FROM_RIGHT if $prev_node == $curr_node->right; FROM_PARENT: last if $next_node = $curr_node->left; FROM_LEFT: $visitor_callback->( $curr_node ); last if $next_node = $curr_node->right; FROM_RIGHT: $next_node = $curr_node->parent; } ( $prev_node, $curr_node ) = ( $curr_node, $next_node ); } }

In this rendition of the algorithm, the reformulation required to implement pre- or post-order traversal is trivial: you just move the callback invocation to the appropriate label.

(It is in fact quite simple to implement all three variants in a single function: just put a call in every branch and make them conditional on an extra parameter, eg. $visitor_callback->( $curr_node ) if $order == -1; where $order == 0 means in-order traversal and in that case the parameter is optional.)

Makeshifts last the longest.

Replies are listed 'Best First'.
Re: Tree traversal without recursion: the tree as a state machine
by dragonchild (Archbishop) on Feb 16, 2007 at 17:21 UTC
    Tree implements an iterative callback for pre-order, post-order, and level-order traversals. If you call traverse() in scalar context, you'll be given a callback which will return the next node when invoked.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Tree traversal without recursion: the tree as a state machine
by pKai (Priest) on Feb 16, 2007 at 22:54 UTC

    It's no coincidence that stacks have a close relationship with trees.

    You can get rid of any stacks whatsoever by keeping a parent pointer in the tree node data structure.

    I wouldn't say you "get rid of any stacks" here.

    While you don't implement the stack as a list in classical ways, the list of "parent pointer(s)" between the root and the current node at any given moment of the traversal forms a stack.

    While traversing, you need no memory other than the current and the previous node/state.

    "previous node/state" obviously refers to the "top of stack", the single point where a stack is accessible.

      Yes, it抯 just a different form of storage of the same data. Obviously, you have to store enough information to find your way around the tree somewhere. If it抯 not one place, it抯 another. However, this algorithm really does not use a stack, at least not if you define a stack in the only way which makes sense ?that is, as a data structure whose defining property is support for the operations of pushing things onto the top and popping them off the top.

      It consumes more space, of course, to keep all those parent pointers around. If you use a stack, they are transient and you only need O(log n) stack space, whereas it抯 O(n) if you store the pointers with the nodes ?another way in which the two approaches are truly distinct.

      What I like about this approach (which is what compelled me to post it) is the exceptional simplicity of the traversal algorithm. When it first occured to me, it seemed too simple to work ?but no, the position and direction of the traverser encodes enough information to guide its path along the entire tree in the right order without any ambiguity.

      Makeshifts last the longest.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://600456]
Approved by Joost
Front-paged by liverpole
help
Chatterbox?
and all is quiet...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (5)
As of 2025-08-07 11:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    hippoepoptai's answer Re: how do I set a cookie and redirect was blessed by hippo!
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.


    殊胜的意思是什么 92年属什么 橄榄油的好处和坏处是什么 悠哉悠哉是什么意思 单核细胞百分比偏高说明什么
    芡实和什么搭配最好 肝藏血是什么意思 otg线是什么 as是什么 无意识是什么意思
    硝酸咪康唑乳膏和酮康唑乳膏有什么区别 名存实亡是什么意思 早上八点半是什么时辰 梦到别人怀孕了是什么意思 梦见头发长长了是什么意思
    肱骨头小囊变什么意思 夏末是什么时候 意味深长的意思是什么 咖啡和什么不能一起吃 尿液很黄是什么原因
    女生排卵是什么意思hcv8jop8ns3r.cn 葡萄套袋前打什么药hcv8jop6ns7r.cn 贪心不足蛇吞象什么意思hcv9jop2ns9r.cn 闲鱼转卖什么意思imcecn.com 苍蝇吃什么hcv8jop1ns5r.cn
    呼吸内镜检查什么zhongyiyatai.com 兼得是什么意思hcv7jop6ns9r.cn 春风什么什么hcv8jop8ns8r.cn 血月代表什么hcv9jop6ns4r.cn 老死不相往来什么意思hcv8jop8ns5r.cn
    菜心又叫什么菜hcv8jop5ns9r.cn 碱性磷酸酶偏高是什么原因helloaicloud.com 外阴瘙痒擦什么药hcv8jop9ns0r.cn who医学上是什么意思weuuu.com 堤防是什么意思hcv9jop3ns5r.cn
    小孩子为什么老是流鼻血hcv8jop6ns2r.cn 最贵的烟是什么hcv8jop1ns2r.cn 白带什么颜色hcv9jop3ns9r.cn 虚张声势是什么生肖hcv8jop5ns4r.cn 8月27号是什么星座bfb118.com
    百度