天龙遍历技能idjava 二叉树遍历怎么使用技能

拒绝访问 | bbs.dnjc8.net | 百度云加速
请打开cookies.
此网站 (bbs.dnjc8.net) 的管理员禁止了您的访问。原因是您的访问包含了非浏览器特征(46e38-ua98).
重新安装浏览器,或使用别的浏览器不使用递归和栈中序遍历二叉树
不使用递归和栈中序遍历二叉树
, 围观1330次
我们知道,在深度搜索遍历的过程中,之所以要用递归或者是用非递归的栈方式,参考,都是因为其他的方式没法记录当前节点的parent,而如果在每个节点的结构里面加个parent 分量显然是不现实的,那么Morris是怎么解决这一问题的呢?好吧,他用得很巧妙,实际上是用叶子节点的空指针来记录当前节点的位置,然后一旦遍历到了叶子节点,发现叶子节点的右指针指向的是当前节点,那么就认为以当前节点的左子树已经遍历完成。Morris 遍历正是利用了 的思想。
以inorder为例,初始化当前节点为root,它的遍历规则如下:
如果当前节点为空,程序退出。
如果当前节点非空,
如果当前节点的左儿子为空,那么输出当前节点,当前节点重置为当前节点的右儿子。
如果当前节点的左儿子非空,找到当前节点左子树的最右叶子节点(此时最右节点的右儿子有两种情况,一种是指向当前节点,一种是为空,你也许感到奇怪,右节点的右儿子怎么可能非空,注意,这里的最右叶子节点只带的是原树中的最右叶子节点。),若其最右叶子节点为空,令其指向当前节点,将当前节点重置为其左儿子,若其最右节点指向当前节点,输出当前节点,将当前节点重置为当前节点的右儿子,并恢复树结构,即将最右节点的右节点再次设置为NULL
代码如下:
#include&stdio.h&
#include&stdlib.h&
struct tNode
struct tNode*
struct tNode*
void MorrisTraversal(struct tNode *root)
struct tNode *current,*
if(root == NULL)
while(current != NULL)
if(current-&left == NULL)
printf(" %d ", current-&data);
current = current-&
/* 找到current的前驱节点 */
pre = current-&
while(pre-&right != NULL && pre-&right != current)
pre = pre-&
/* 将current节点作为其前驱节点的右孩子 */
if(pre-&right == NULL)
pre-&right =
current = current-&
/* 恢复树的原有结构,更改right 指针 */
pre-&right = NULL;
printf(" %d ",current-&data);
current = current-&
} /* End of if condition pre-&right == NULL */
} /* End of if condition current-&left == NULL*/
} /* End of while */
struct tNode* newtNode(int data)
struct tNode* tNode = (struct tNode*)
malloc(sizeof(struct tNode));
tNode-&data =
tNode-&left = NULL;
tNode-&right = NULL;
return(tNode);
int main()
/* 构建树结构如下:
struct tNode *root = newtNode(1);
root-&left
= newtNode(2);
root-&right
= newtNode(3);
root-&left-&left
= newtNode(4);
root-&left-&right = newtNode(5);
MorrisTraversal(root);
参考:http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/
/obama/p/3262054.html
您可能还会对这些文章感兴趣!二叉树前序、中序、后序遍历非递归写法的透彻解析
在前两篇文章二叉树和二叉搜索树中已经涉及到了二叉树的三种遍历。递归写法,只要理解思想,几行代码。可是非递归写法却很不容易。这里特地总结下,透彻解析它们的非递归写法。其中,中序遍历的非递归写法最简单,后序遍历最难。我们的讨论基础是这样的:
//Binary Tree Node
typedef struct node
struct node*
struct node*
首先,有一点是明确的:非递归写法一定会用到栈,这个应该不用太多的解释。我们先看中序遍历:
中序遍历的递归定义:先左子树,后根节点,再右子树。如何写非递归代码呢?一句话:让代码跟着思维走。我们的思维是什么?思维就是中序遍历的路径。假设,你面前有一棵二叉树,现要求你写出它的中序遍历序列。如果你对中序遍历理解透彻的话,你肯定先找到左子树的最下边的节点。那么下面的代码就是理所当然的:
中序代码段(i)
BTNode* p =
//p指向树根
//STL中的栈
//一直遍历到左子树最下边,边遍历边保存根节点到栈中
s.push(p);
保存一路走过的根节点的理由是:中序遍历的需要,遍历完左子树后,需要借助根节点进入右子树。代码走到这里,指针p为空,此时无非两种情况:
说明:vcD4KPHA+PC9wPgoKyc/NvNbQ1ru4+LP2wcux2NKqtcS92rXjus2x36OsxuTL/LXEsd+6zb3atePT68zWwtvO3rnYo6yyu7HYu62z9qGjxOO/ycTcyM/Oqs28YdbQ1+69/LGjtOa92rXjy+Oyu7XDyse4+b3ateOho8jnufvE47+0uf3K96Gitv6y5sr3u/m0oaOsyrnTw8Cps+S2/rLmyve1xLjFxO6jrL7Nv8nS1L3iys2ho9fc1q6jrLK708O+wL3h1eK49sO709DS4tLlzsrM4qGjPGJyPgrV+7j2tv6y5sr31rvT0NK7uPa4+b3ateO1xMfpv/a/ydLUu661vc28YaGjCtfQz7jP68/ro6y2/rLmyve1xNfz19PK96Os1+7PwrHfyseyu8rHyc/NvMG91tbH6b/2o7+yu7nc1PXR+aOstMvKsba80qqz9tW7o6yyorfDzsq4w73ateOho9XiuPa92rXjvs3Kx9bQ0PLQ8sHQtcS12tK7uPa92rXjoaO4+b7dztLDx7XEy7zOrKOstPrC69OmuMPKx9Xi0fmjuiAgCjxwcmUgY2xhc3M9"brush:">p = s.top();
我们的思维接着走,两图情形不同得区别对待:
1.图a中访问的是一个左孩子,按中序遍历顺序,接下来应访问它的根节点。也就是图a中的另一个节点,高兴的是它已被保存在栈中。我们只需这样的代码和上一步一样的代码:
p = s.top();
左孩子和根都访问完了,接着就是右孩子了,对吧。接下来只需一句代码:p=p->在右子树中,又会新一轮的代码段(i)、代码段(ii)……直到栈空且p空。
2.再看图b,由于没有左孩子,根节点就是中序序列中第一个,然后直接是进入右子树:p=p->在右子树中,又会新一轮的代码段(i)、代码段(ii)……直到栈空且p空。
思维到这里,似乎很不清晰,真的要区分吗?根据图a接下来的代码段(ii)这样的:
p = s.top();
p = s.top();
根据图b,代码段(ii)又是这样的:
p = s.top();
我们可小结下:遍历过程是个循环,并且按代码段(i)、代码段(ii)构成一次循环体,循环直到栈空且p空为止。
不同的处理方法很让人抓狂,可统一处理吗?真的是可以的!回顾扩充二叉树,是不是每个节点都可以看成是根节点呢?那么,代码只需统一写成图b的这种形式。也就是说代码段(ii)统一是这样的:
中序代码段(ii)
p = s.top();
口说无凭,得经的过理论检验。
图a的代码段(ii)也可写成图b的理由是:由于是叶子节点,p=-=p->之后p肯定为空。为空,还需经过新一轮的代码段(i)吗?显然不需。(因为不满足循环条件)那就直接进入代码段(ii)。看!最后还是一样的吧。还是连续出栈两次。看到这里,要仔细想想哦!相信你一定会明白的。
这时写出遍历循环体就不难了:
BTNode* p =
while (!s.empty() || p)
//代码段(i)一直遍历到左子树最下边,边遍历边保存根节点到栈中
s.push(p);
//代码段(ii)当p为空时,说明已经到达左子树最下边,这时需要出栈了
if (!s.empty())
p = s.top();
cout << setw(4) <
//进入右子树,开始新的一轮左子树遍历(这是递归的自我实现)
仔细想想,上述代码是不是根据我们的思维走向而写出来的呢?再加上边界条件的检测,中序遍历非递归形式的完整代码是这样的:
中序遍历代码一
//中序遍历
void InOrderWithoutRecursion1(BTNode* root)
if (root == NULL)
BTNode* p =
while (!s.empty() || p)
//一直遍历到左子树最下边,边遍历边保存根节点到栈中
s.push(p);
//当p为空时,说明已经到达左子树最下边,这时需要出栈了
if (!s.empty())
p = s.top();
cout << setw(4) <
//进入右子树,开始新的一轮左子树遍历(这是递归的自我实现)
恭喜你,你已经完成了中序遍历非递归形式的代码了。回顾一下难吗?
接下来的这份代码,本质上是一样的,相信不用我解释,你也能看懂的。
中序遍历代码二
//中序遍历
void InOrderWithoutRecursion2(BTNode* root)
if (root == NULL)
BTNode* p =
while (!s.empty() || p)
s.push(p);
p = s.top();
cout << setw(4) <
前序遍历的递归定义:先根节点,后左子树,再右子树。有了中序遍历的基础,不用我再像中序遍历那样引导了吧。
首先,我们遍历左子树,边遍历边打印,并把根节点存入栈中,以后需借助这些节点进入右子树开启新一轮的循环。还得重复一句:所有的节点都可看做是根节点。根据思维走向,写出代码段(i):
前序代码段(i)
//边遍历边打印,并存入栈中,以后需要借助这些根节点(不要怀疑这种说法哦)进入右子树
cout << setw(4) <
s.push(p);
接下来就是:出栈,根据栈顶节点进入右子树。
前序代码段(ii)
//当p为空时,说明根和左子树都遍历完了,该进入右子树了
if (!s.empty())
p = s.top();
同样地,代码段(i)(ii)构成了一次完整的循环体。至此,不难写出完整的前序遍历的非递归写法。
前序遍历代码一
void PreOrderWithoutRecursion1(BTNode* root)
if (root == NULL)
BTNode* p =
while (!s.empty() || p)
//边遍历边打印,并存入栈中,以后需要借助这些根节点(不要怀疑这种说法哦)进入右子树
cout << setw(4) <
s.push(p);
//当p为空时,说明根和左子树都遍历完了,该进入右子树了
if (!s.empty())
p = s.top();
下面给出,本质是一样的另一段代码:
前序遍历代码二
//前序遍历
void PreOrderWithoutRecursion2(BTNode* root)
if (root == NULL)
BTNode* p =
while (!s.empty() || p)
cout << setw(4) <
s.push(p);
p = s.top();
在二叉树中使用的是这样的写法,略有差别,本质上也是一样的:
前序遍历代码三
void PreOrderWithoutRecursion3(BTNode* root)
if (root == NULL)
BTNode* p =
s.push(root);
while (!s.empty())
//循环结束条件与前两种不一样
//这句表明p在循环中总是非空的
cout << setw(4) <
栈的特点:先进后出
先被访问的根节点的右子树后被访问
if (p->rchild)
s.push(p->rchild);
if (p->lchild)
{//左子树访问完了,访问右子树
p = s.top();
最后进入最难的后序遍历:
后序遍历递归定义:先左子树,后右子树,再根节点。后序遍历的难点在于:需要判断上次访问的节点是位于左子树,还是右子树。若是位于左子树,则需跳过根节点,先进入右子树,再回头访问根节点;若是位于右子树,则直接访问根节点。直接看代码,代码中有详细的注释。
后序遍历代码
//后序遍历
void PostOrderWithoutRecursion(BTNode* root)
if (root == NULL)
//pCur:当前访问节点,pLastVisit:上次访问节点
BTNode* pCur, *pLastV
pLastVisit = NULL;
//先把pCur移动到左子树最下边
while (pCur)
s.push(pCur);
pCur = pCur->
while (!s.empty())
//走到这里,pCur都是空,并已经遍历到左子树底端(看成扩充二叉树,则空,亦是某棵树的左孩子)
pCur = s.top();
//一个根节点被访问的前提是:无右子树或右子树已被访问过
if (pCur->rchild == NULL || pCur->rchild == pLastVisit)
cout << setw(4) <
//修改最近被访问的节点
pLastVisit = pC
/*这里的else语句可换成带条件的else if:
else if (pCur->lchild == pLastVisit)//若左子树刚被访问过,则需先进入右子树(根节点需再次入栈)
因为:上面的条件没通过就一定是下面的条件满足。仔细想想!
//根节点再次入栈
s.push(pCur);
//进入右子树,且可肯定右子树一定不为空
pCur = pCur->
while (pCur)
s.push(pCur);
pCur = pCur->
思维和代码之间总是有巨大的鸿沟。通常是思维正确,清楚,但却不易写出正确的代码。要想越过这鸿沟,只有多尝试、多借鉴,别无它法。
转载请注明出处,本文地址:https://blog.csdn.net/zhangxiangdavaid/article/details/
专栏目录:数据结构与算法目录还没有帐号? 赶紧
用户名:&&密码:登录注册
天龙八部 技能遍历和背包遍历的问题 有图 附送技能二叉树基址
UID:84572
在线时间23小时
金钱110威望126贡献值0社区警告0诚信值0账号服务0热心值0
&1.虚函数下手冒失找不到技能冷却时间或状态。&2.这个基址遍历背包主要是为了配合使用喝药CALL(问题出来了,易语言读取出来的指针指向地址,怎么才回溯当前地址)& 如下图:&=700) window.open('/bbs/attachment/Fid_148/148_bc63412ecf.jpg?12');" style="max-width:700max-height:700" onload="if(is_ie6&&this.offsetWidth>700)this.width=700;">易语言读出来的都是后面的数值,怎么才能读出前面的71B1E6A4?&=700) window.open('/bbs/attachment/Fid_148/148_84572_cef4a36934eb7de.jpg?58');" style="max-width:700max-height:700" onload="if(is_ie6&&this.offsetWidth>700)this.width=700;">=700) window.open('/bbs/attachment/Fid_148/148_72ccd1117a.jpg?70');" style="max-width:700max-height:700" onload="if(is_ie6&&this.offsetWidth>700)this.width=700;">& & &另附送:最新的技能二叉树基址&[125A5B4]+34]树顶&*4 父节点&+15 数组标志&左子树 0&右子树 8&+10 子节点&+C 技能名字&+4 技能ID&子节点+50 判断已学技能&&
UID:84572
在线时间23小时
金钱110威望126贡献值0社区警告0诚信值0账号服务0热心值0
唉 学了十几天 人都整耙了 找个二叉树都找了我一天,现奉献给大家,老鸟别笑,小弟不才,希望大侠帮忙解答一下我的问题。&&做挂 找基址找CALL容易 找出来要写的出来才是硬道理啊,现在我基本要找的都找完了,易语言写挂太难了,不知道怎么写,今天写个简单的遍历二叉树都研究一天,真不知道后面的该怎么写~~~~~~~~
UID:82293
在线时间117小时
金钱509威望180贡献值10社区警告0诚信值0账号服务0热心值0
&&易语言是出挂最快的了吧
UID:49918
在线时间229小时
金钱1634威望19贡献值44社区警告0诚信值0账号服务0热心值0
易语言的代码都是现成的,怎么会难写呢?
Hello,Baby
UID:84185
在线时间197小时
金钱1453威望1493贡献值0社区警告0诚信值0账号服务0热心值2
技能ID 数据是否正确啊? 那个打坐的ID好像不应该那么大
UID:79586
在线时间88小时
金钱843威望136贡献值40社区警告0诚信值0账号服务0热心值2
找数据的时间是写挂时间的10倍
UID:84380
在线时间77小时
金钱118威望451贡献值0社区警告0诚信值0账号服务0热心值0
=700) window.open('/bbs/attachment/Fid_148/148_84380_cd05d3ea7088229.jpg?27');" style="max-width:700max-height:700" onload="if(is_ie6&&this.offsetWidth>700)this.width=700;">
UID:83300
在线时间41小时
金钱568威望669贡献值0社区警告0诚信值0账号服务0热心值0
还是第一次听找数据容易写程序难的,我用微软的东西都没觉得难写,更何况易。反倒是找call找基址工作量忒大了,最好别找软柿子捏,你试试拿块砖头啃啃就知道了。
UID:84059
在线时间20小时
金钱565威望1284贡献值0社区警告0诚信值0账号服务0热心值0
要读取前面的地址不难,但要我用易语言写,我就不会了
UID:84563
在线时间18小时
金钱287威望497贡献值0社区警告0诚信值0账号服务0热心值0
我才刚刚开始,楼主加油,我才开始学汇编而已!
访问内容超出本站范围,不能确定是否安全
Powered by & Copyright Time now is:09-05 12:27 &
版权所有 Gzip enabled
Total 0.040992(s) query 1, Time now is:09-05 12:28, Gzip enabled:怎么正确理解二叉树的遍历_百度经验
&&&&&&互联网怎么正确理解二叉树的遍历听语音1234
百度经验:二叉树就是一种树形存储结构,每个节点最多有两个子树。百度经验:1在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树的遍历分为三类:前序遍历、中序遍历和后序遍历。2(1)前序遍历& & 先访问根节点,再遍历左子树,最后遍历右子树;并且在遍历左右子树时,仍需先访问根节点,然后遍历左子树,最后遍历右子树。上图的前序遍历如下。3(2)中序遍历先遍历左子树、然后访问根节点,最后遍历右子树;并且在遍历左右子树的时候。仍然是先遍历左子树,然后访问根节点,最后遍历右子树。前图的中序遍历如下。4(3)后序遍历先遍历左子树,然后遍历右子树,最后访问根节点;同样,在遍历左右子树的时候同样要先遍历左子树,然后遍历右子树,最后访问根节点。前图后序遍历结果如下。5关于的二叉树的遍历,仔细看完这一篇文章基本就可以完全理解了。END经验内容仅供参考,如果您需解决具体问题(尤其法律、医学等领域),建议您详细咨询相关领域专业人士。作者声明:本篇经验系本人依照真实经历原创,未经许可,谢绝转载。投票(20)已投票(20)有得(0)我有疑问(0)◆◆说说为什么给这篇经验投票吧!我为什么投票...你还可以输入500字◆◆只有签约作者及以上等级才可发有得&你还可以输入1000字◆◆如对这篇经验有疑问,可反馈给作者,经验作者会尽力为您解决!你还可以输入500字相关经验40402013热门杂志第1期你不知道的iPad技巧3603次分享第1期win7电脑那些事6301次分享第2期新人玩转百度经验1270次分享第1期Win8.1实用小技巧2579次分享第1期小白装大神1769次分享◆请扫描分享到朋友圈}

我要回帖

更多关于 java二叉树遍历算法 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信