二叉树的遍历:

先序,中序,后序;

二叉树的遍历有三种常见的方法,

最简单的实现就是递归调用,

另外就是飞递归的迭代调用,

最后还有O(1)空间的morris遍历;

二叉树的结构定义:

 struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

1.先序遍历:

递归:

 void preOrderRecursive(TreeNode *root) {
if (!root)
return;
cout << root->val << " ";
preOrderRecursive(root->left);
preOrderRecursive(root->right);
}

迭代:

迭代要用到栈来保存父亲结点,

先序遍历,所有访问过的结点都先输出,

先遍历当前结点和当前结点的左子树,一直到左子树的最左边的结点,

遍历过的这些结点都入栈,左孩子为空时,栈顶元素设为当前结点,出栈,

然后把当前结点设为该节点的右孩子,循环一直到当前结点为空且栈也为空。

 void preOrderIterative(TreeNode *root) {
if (!root)
return;
stack<TreeNode*> stk;
TreeNode *cur = root;
while (cur || !stk.empty()) {
while (cur) {
cout << cur->val << " ";
stk.push(cur);
cur = cur->left;
}
if (!stk.empty()) {
cur = stk.top();
stk.pop();
cur = cur->right;
}
}
}
/*
* 模拟递归
* */
void preOrderIterative1(TreeNode *root) {
if (!root)
return;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* tp = stk.top();
stk.pop();
cout << tp->val << " ";
if (tp->right)
stk.push(tp->right);
if (tp->left)
stk.push(tp->left);
}
}

Morris方法:

1.当前结点的左孩子为空,输出当前结点,并设置当前结点的右孩子为当前结点;

2.当前结点的左孩子不为空:

  a.找到当前结点中序遍历的前驱结点,即为该节点的左孩子的最右边的结点,前驱结点的右孩子为空,则设置前驱结点的右孩子为当前结点,并输出当前结点,设当前结点的左孩子为当前结点;

  b.前驱结点的右孩子为当前结点,则恢复前驱结点的右孩子为NULL,设当前结点的右孩子为当前结点;

 void preOrderMorris(TreeNode *root) {
if (!root)
return;
TreeNode* cur = root;
TreeNode* pre = NULL;
while (cur) {
if (cur->left == NULL) {
cout << cur->val << " ";
cur = cur->right;
}
else {
pre = cur->left;
while (pre->right != NULL && pre->right != cur)
pre = pre->right;
if (pre->right == NULL) {
cout << cur->val << " ";
pre->right = cur;
cur = cur->left;
}
else {
pre->right = NULL;
cur = cur->right;
}
}
}
}

2.中序遍历:

递归:

 void inOrderRecursive(TreeNode *root) {
if (!root)
return;
inOrderRecursive(root->left);
cout << root->val << " ";
inOrderRecursive(root->right);
}

迭代:

1.如果当前结点的左孩子不为空,则把当前结点的左孩子入栈,知道当前结点的左孩子为空;

2.如果栈不为空,则出栈,栈顶元素为当前结点,输出当前结点,并把当前结点的右孩子设为当前结点;

重复1,2直到当前结点为NULL且栈也为空;

 void inOrderIterative(TreeNode *root) {
if (!root)
return;
stack<TreeNode*> stk;
TreeNode *cur = root;
while (cur || !stk.empty()) {
while (cur) {
stk.push(cur);
cur = cur->left;
}
if (!stk.empty()) {
cur = stk.top();
stk.pop();
cout << cur->val << " ";
cur = cur->right;
}
}
}

Morris方法:

和先序遍历的过程类似,只不过输出结点的位置不一样,中序遍历是在2.b中,也就是前驱结点的右孩子为当前结点时,即当前结点的左子树都已经遍历完成时,输出当前结点;

 void inOrderMorris(TreeNode *root) {
if (!root)
return;
TreeNode* cur = root;
TreeNode* pre = NULL;
while (cur) {
if (cur->left == NULL) {
cout << cur->val << " ";
cur = cur->right;
}
else {
pre = cur->left;
while (pre->right != NULL && pre->right != cur)
pre = pre->right;
if (pre->right == NULL) {
pre->right = cur;
cur = cur->left;
}
else {
cout << cur->val << " ";
pre->right = NULL;
cur = cur->right;
}
}
}
}

3.后序遍历:

递归:

 void postOrderRecursive(TreeNode *root) {
if (!root)
return;
postOrderRecursive(root->left);
postOrderRecursive(root->right);
cout << root->val << " ";
}

迭代:

后序遍历比先序、中序都要复杂,

第一种迭代方法,可以用两个栈来模拟递归的遍历;

栈1初始化时把根节点入栈,

1.栈1出栈,把出栈的元素加入栈2,然后把该元素的左孩子(如果不为空),右孩子(如果不为空)加入栈1,知道栈1为空;

2.栈2出栈直到空,每次出栈时输出栈顶元素;

通过两个栈,保证了栈2中的元素顺序,

第二种迭代方法,

用一个结点保存访问过的最后一个结点pre,如果pre为栈顶元素的右孩子,则说明栈顶元素的右子树已经遍历过了,直接输出栈顶元素,并把当前结点设为NULL,并更新pre为出栈的元素;

1.如果当前结点存在,则一直向左遍历,入栈遍历的元素,直到结点为空;

2.栈不为空时,出栈,当前结点为栈顶元素,

如果当前结点的右孩子不为空且不为pre,说明当前结点的右子树没有遍历过,设置当前结点为该节点的右孩子,

如果右孩子为空或者为pre,直接输出当前结点,更新pre为当前结点,并设当前结点为NULL,

重复1,2直到当前结点为NULL并且栈为空;

 void postOrderIterative(TreeNode *root) {
if (!root)
return;
stack<TreeNode*> stk;
TreeNode *cur = root;
TreeNode *pre = NULL;
while (cur || !stk.empty()) {
while (cur) {
stk.push(cur);
cur = cur->left;
}
if (!stk.empty()) {
cur = stk.top();
if (cur->right != NULL && cur->right != pre) {
cur = cur->right;
}
else {
cout << cur->val << " ";
pre = cur;
stk.pop();
cur = NULL;
}
}
}
}
/*
* 双栈法
*/
void postOrderIterative1(TreeNode *root) {
if (!root)
return;
stack<TreeNode*> stk1, stk2;
TreeNode *cur;
stk1.push(root);
while (!stk1.empty()) {
cur = stk1.top();
stk1.pop();
stk2.push(cur);
if (cur->left)
stk1.push(cur->left);
if (cur->right)
stk1.push(cur->right);
}
while (!stk2.empty()) {
cout << stk2.top()->val << " ";
stk2.pop();
}
}

Morris方法:

morris方法的后序遍历较为复杂,因为需要逆序输出右孩子到父亲结点;

遍历过程与先序与中序类似,

当前驱结点的右孩子为当前结点时,左子树已经遍历完成,逆序输出当前结点的左孩子到前驱结点;

类似于链表的反转,不过反转输出之后,记得要反转回来。

 void reverse(TreeNode *begin, TreeNode *end) {
if (begin == end)
return;
TreeNode *pre = begin;
TreeNode *cur = begin->right;
TreeNode *next;
while (pre != end) {
temp = cur->right;
cur->right = pre;
pre = cur;
cur = temp;
}
} void traversalReversal(TreeNode *begin, TreeNode *end) {
reverse(begin, end);
TreeNode *it = end;
while (true) {
cout << it->val << " ";
if (it == begin)
break;
it = it->right;
}
reverse(end, begin);
} void postOrderMorris(TreeNode *root) {
if (!root)
return;
TreeNode dump();
dump.left = root;
TreeNode *cur = &dump;
TreeNode *pre = NULL;
while (cur) {
if (cur->left == NULL) {
cur = cur->right;
}
else {
pre = cur->left;
while (pre->right != NULL && pre->right != cur)
pre = pre->right;
if (pre->right == NULL) {
pre->right = cur;
cur = cur->left;
}
else {
traversalReverse(cur->left, pre);
pre->right = NULL;
cur = cur->right;
}
}
}
}

参考:

http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html

http://blog.csdn.net/hackbuteer1/article/details/6583988

二叉树的遍历(递归,迭代,Morris遍历)的更多相关文章

  1. 二叉树的建立&&前中后遍历(递归实现)&&层次遍历

    下面代码包含了二叉树的建立过程,以及三种遍历方法了递归实现,代码中还利用队列实现了层次遍历. import java.util.LinkedList; import java.util.Queue; ...

  2. LeetCode:二叉树的非递归中序遍历

    第一次动手写二叉树的,有点小激动,64行的if花了点时间,上传leetcode一次点亮~~~ /* inorder traversal binary tree */ #include <stdi ...

  3. Python实现二叉树的非递归先序遍历

    思路: 1. 使用列表保存结果: 2. 使用栈(列表实现)存储结点: 3. 当根结点存在,保存结果,根结点入栈: 4. 将根结点指向左子树: 5. 根结点不存在,栈顶元素出栈,并将根结点指向栈顶元素的 ...

  4. Python实现二叉树的非递归中序遍历

    思路: 1. 使用一个栈保存结点(列表实现): 2. 如果结点存在,入栈,然后将当前指针指向左子树,直到为空: 3. 当前结点不存在,则出栈栈顶元素,并把当前指针指向栈顶元素的右子树: 4. 栈不为空 ...

  5. Morris 遍历实现二叉树的遍历

    Morris 遍历实现二叉树的遍历 作者:Grey 原文地址: 博客园:Morris 遍历实现二叉树的遍历 CSDN:Morris 遍历实现二叉树的遍历 说明 Morris 遍历可以实现二叉树的先,中 ...

  6. 经典算法 Morris遍历

    内容: 1.什么是morris遍历 2.morris遍历规则与过程 3.先序及中序 4.后序 5.morris遍历时间复杂度分析 1.什么是morris遍历 关于二叉树先序.中序.后序遍历的递归和非递 ...

  7. 二叉树的遍历(递归,迭代,Morris遍历)

    二叉树的三种遍历方法: 先序,中序,后序,这三种遍历方式每一个都可以用递归,迭代,Morris三种形式实现,其中Morris效率最高,空间复杂度为O(1). 主要参考博客: 二叉树的遍历(递归,迭代, ...

  8. [转载]Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)

    本文主要解决一个问题,如何实现二叉树的前中后序遍历,有两个要求: 1. O(1)空间复杂度,即只能使用常数空间: 2. 二叉树的形状不能被破坏(中间过程允许改变其形状). 通常,实现二叉树的前序(pr ...

  9. Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)——无非是在传统遍历过程中修改叶子结点加入后继结点信息(传统是stack记录),然后再删除恢复

    先看看线索二叉树 n个结点的二叉链表中含有n+1(2n-(n-1)=n+1)个空指针域.利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索 ...

随机推荐

  1. 独立博客开张!有关读书、GTD和IT方面的内容将发布在新网站上

    2015年自己建个独立博客http://www.shenlongbin.com,以后与读书.GTD和IT技术有关的主题都放在个人博客中,2015年计划基本制定,请移步到这里. 感谢博客园提供了如此优秀 ...

  2. 【读书笔记】iOS-UIFont-如何知道字体的PostScript名称

    一,名词解释 PostScript字体: 按 PostScript 页面描述语言 (PDL) 规则定义的字体,并且只能在 PostScript 兼容的打印机上打印. 二,打开Launchpad---- ...

  3. 【转】Visual Studio项目相对路径的设置,实用

    这篇文章主要写给那些以前没有用过VS的新手,同时也是自己学习经验的积累,高手就不用看了以免浪费您宝贵的时间. 在VS的工程中常常要设置头文件的包含路径,当然你可以使用绝对路径,但是如果你这样设置了你只 ...

  4. iOS开发融云即时通讯集成详细步骤

    1.融云即时通讯iOS SDK下载地址   http://rongcloud.cn/downloads  选择iOS   SDK下载 2.进行应用开发之前,需要先在融云开发者平台创建应用,如果您已经注 ...

  5. java实现栈和队列

    class Node { int val; Node next; Node(int x) { val = x; next = null; } } class Stack { Node top; pub ...

  6. 如何获取QQ里的截图app?

    电脑系统平台:OS X EI Capitan 10.11 在以前的旧的QQ版本,QQ的截图的偏好还有一个开机自启动的选项: 现在新的版本,却没有了"开机自动运行"的选项,然而有时候 ...

  7. 谷歌浏览器Chrome安装,报0x80004002 等错误时,乖乖的这么解决!亲测有效。

    1.卸载现有的谷歌浏览器,推荐使用360卸载(最好清扫残留文件) 2.在百度搜索中下载一个离线的 chrome浏览器(准备在安全模式先安装),记住,一定不能是谷歌官网的离线包(我屡次尝试安装官方离线包 ...

  8. Java 读取txt文件,读取结果保存到数据库

    需求:有一个很大的txt文件(1,000,000条数据),从txt中读取有用数据库后保存到Oracle数据库中 利用Java实现: 1.加载文件后一行一行读取 2.数据库连接后按行插入到数据库 pac ...

  9. 解决easy ui两次请求服务器的问题

    目前该问题已经在1.4.1版本中解决了 本文引用自:http://www.cnblogs.com/Reaver/p/4056770.html,原文博主:flyreaver 我在使用过程中遇到了easy ...

  10. hdu 4612 Warm up 双连通+树形dp思想

    Warm up Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total S ...