1. 非递归遍历二叉树算法 (使用stack)

以非递归方式对二叉树进行遍历的算法需要借助一个栈来存放访问过得节点。

(1) 前序遍历

从整棵树的根节点开始,对于任意节点V,访问节点V并将节点V入栈,并判断节点V的左子节点L是否为空。若L不为空,则将L置为当前节点V;若L为空,则取出栈顶节点,并将栈顶结点的右子节点置为当前节点V。重复上述操作,直到当前节点V为空并且栈为空,遍历结束。

(2) 中序遍历

从整棵树的根节点开始,对于任意节点V,判断其左子节点L是否为空。若L不为空,则将V入栈并将L置为当前节点V;若L为空,则取出栈顶节点并访问该栈顶节点,然后将其右子节点置为当前节点V。重复上述操作,直到当前节点V为空节点且栈为空,遍历结束。

(3) 后序遍历

首先将整颗二叉树的根节点入栈。取栈顶节点V,若V不存在左子节点和右子节点,或V存在左子节点或右子节点但其左子节点和右子节点都被访问过了,则访问节点V,并将V从栈中弹出。若非上述两种情况,则将V的右子节点和左子节点(注意先右后左,这样出栈时才能先左后右)依次入栈。重复上述操作,直到栈为空,遍历结束。

2. 二叉树递归与非递归遍历代码

 #include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define Stack_increment 20
#define Stack_Size 100 typedef struct Tree
{
char data;
struct Tree *lchild;
struct Tree *rchild;
}Node; Node* createBinaryTree()
{
Node *root;
char ch;
scanf("%c", &ch); if (ch == '#')
{
root = NULL;
}
else
{
root = (Node *)malloc(sizeof(Node));
root -> data = ch;
root -> lchild = createBinaryTree();
root -> rchild = createBinaryTree();
} return root;
} typedef struct
{
int top;
Node* arr[Stack_Size];
}Stacktree; void InitStack(Stacktree *S)
{
S->top = 0;
} void Push(Stacktree* S, Node* x)
{
int top1 = S -> top;
if (x -> data == '#')
{
return;
}
else
{
S -> arr[top1++] = x;
S -> top++;
}
} int Pop(Stacktree *S)
{
int top = S -> top;
if (S->top == 0)
{
return 0;
}
else
{
--(S->top);
return 1;
}
} Node* GetTop(Stacktree *S)
{
int top1 = S -> top;
Node*p;
p = S -> arr[top1--];
return p;
} Node* GetTop1(Stacktree *S)
{
int top1 = S -> top;
Node*p;
top1--;
p = S -> arr[top1];
return p;
} int IsEmpty(Stacktree *S)
{
return(S->top == 0 ? 1 : 0);
} void preorderRecursive(Node *p )
{
if (p != NULL)
{
printf("%c ", p -> data);
preorderRecursive(p -> lchild);
preorderRecursive(p -> rchild);
}
} void inorderRecursive(Node *p )
{
if (p != NULL)
{
inorderRecursive(p -> lchild);
printf("%c ", p -> data);
inorderRecursive(p -> rchild);
}
} void postorderRecursive(Node *p )
{
if (p != NULL)
{
postorderRecursive(p -> lchild);
postorderRecursive(p -> rchild);
printf("%c ", p -> data);
}
} void preordernotRecursive(Node *p)
{
if(p)
{
Stacktree stree ;
InitStack(&stree);
Node *root = p;
while(root != NULL || !IsEmpty(&stree))
{
while(root != NULL)
{
printf("%c ", root->data);
Push(&stree, root);
root = root -> lchild;
} if(!IsEmpty(&stree))
{
Pop(&stree);
root = GetTop(&stree);
root = root -> rchild;
}
}
}
} void inordernotRecursive(Node *p)
{
if(p)
{
Stacktree stree;
InitStack(&stree);
Node *root = p;
while(root != NULL || !IsEmpty(&stree))
{
while(root != NULL)
{
Push(&stree, root);
root = root -> lchild;
} if(!IsEmpty(&stree))
{
Pop(&stree);
root = GetTop(&stree);
printf("%c ", root -> data);
root = root -> rchild;
}
}
}
} void postordernotRecursive(Node *p)
{
Stacktree stree;
InitStack(&stree); Node *root;
Node *pre = NULL; Push(&stree, p); while (!IsEmpty(&stree))
{
root = GetTop1(&stree); if ((root -> lchild == NULL && root -> rchild == NULL) || (pre != NULL && (pre == root -> lchild || pre == root -> rchild)))
{
printf("%c ", root -> data);
Pop(&stree);
pre = root;
} else
{
if (root -> rchild != NULL)
{
Push(&stree, root -> rchild);
} if (root -> lchild != NULL)
{
Push(&stree, root -> lchild);
}
} }
} void main()
{ printf("请输入二叉树,'#'为空\n");
Node *root = createBinaryTree(); printf("\n递归先序遍历:\n");
preorderRecursive(root); printf("\n递归中序遍历:\n");
inorderRecursive(root); printf("\n递归后序遍历:\n");
postorderRecursive(root); printf("\n非递归先序遍历\n");
preordernotRecursive(root); printf("\n非递归中序遍历\n");
inordernotRecursive(root); printf("\n非递归后序遍历\n");
postordernotRecursive(root); getchar();
getchar();
}

(代码中的top是栈顶元素的上一位的index,不是栈顶元素的index~)

input:

ABC##D##E##

output:

递归先序遍历:

A B C D E

递归中序遍历:

C B D A E

递归后序遍历:

C D B E A

非递归先序遍历:

A B C D E

非递归中序遍历:

C B D A E

非递归后序遍历:

C D B E A

3. Morris Traversal (遍历二叉树无需stack)

Morris Traversal 是一种非递归无需栈仅在常量空间复杂度的条件下即可实现二叉树遍历的一种很巧妙的方法。该方法的实现需要构造一种新型的树结构,Threaded Binary Tree.

3.1 Threaded Binary Tree 定义

Threaded binary tree: A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor of the node (if it exists), and all left child pointers that would normally be null point to the inorder predecessor of the node. ~WIkipedia

Threaded binary tree 的构造相当于将所有原本为空的右子节点指向了中序遍历的该点的后续节点,把所有原本为空的左子节点都指向了中序遍历的该点前序节点。如图1所示。

那么通过这种方式,对于当前节点cur, 若其右子树为空,(cur -> right = NULL),那么通过沿着其pre指针,即可返回其根节点继续遍历。

比如对于图1中的节点A,其右孩子为空,则说明以A为根节点的子树遍历完成,沿着其pre指针可以回到A的根节点B,继续遍历。这里的pre指针相当于保存了当前节点的回溯的位置信息。

 

图1. Threaded binary tree         图2. Threaded tree构造及遍历算法图示

3.2 Threaded Binary Tree 算法实现

3.2.1 算法描述

1. 初始化指针cur = root

2. while (cur != NULL)

2.1 if cur -> left == NULL

a) print(cur -> val)

b) cur = cur -> right

2.2 else if cur -> left != NULL

将pre 指向cur 的左子树中的 最右子节点 (并保证不指回cur)

2.2.1 if pre -> right == NULL

a) pre -> right = cur

b) cur = cur -> left

2.2.2 else if pre -> right != NULL (说明pre -> right是用于指回cur节点的指针)

a) 将pre -> right 置空

      b) print(cur -> val)

         c) cur = cur -> right

3.2.2 代码实现 (中序)

 # include <bits/stdc++.h>
using namespace std; struct TreeNode
{
int val;
struct TreeNode *right;
struct TreeNode *left;
TreeNode(int x): val(x), left(NULL), right(NULL) {}
}; vector<int> inorderTraversal(TreeNode *root)
{
vector<int> res;
if(!root) return res;
TreeNode *cur, *pre;
cur = root; while(cur)
{
if(cur -> left == NULL)
{
res.push_back(cur -> val);
cur = cur -> right;
} else if(cur -> left != NULL)
{
pre = cur -> left;
while(pre -> right && pre -> right != cur) pre = pre -> right;
if(pre -> right == NULL)
{
pre -> right = cur;
cur = cur -> left;
}
else if(pre -> right != NULL)
{
pre -> right = NULL;
res.push_back(cur -> val);
cur = cur -> right;
}
}
}
return res;
} int main()
{
vector<int> res;
TreeNode *node1 = new TreeNode();
TreeNode *node2 = new TreeNode();
TreeNode *node3 = new TreeNode();
TreeNode *node4 = new TreeNode();
node1 -> left = node2;
node2 -> left = node3;
node3 -> right = node4;
inorderTraversal(node1);
res = inorderTraversal(node1);
vector<int>::iterator it;
for(it = res.begin(); it != res.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
delete node1; delete node2;
delete node3; delete node4;
return ;
} // 3 4 2 1

参考:

1. 以先序、中序、后序的方式递归非递归遍历二叉树:https://blog.csdn.net/asd20172016/article/details/80786186

2. Morris Traversal: [LeetCode] Binary Tree Inorder Traversal 二叉树的中序遍历: https://www.cnblogs.com/grandyang/p/4297300.html

3. [LeetCode] Recover Binary Search Tree 复原二叉搜索树: https://www.cnblogs.com/grandyang/p/4298069.html

4. Wikipedia: Threaded binary tree: https://en.wikipedia.org/wiki/Threaded_binary_tree

[Alg] 二叉树的非递归遍历的更多相关文章

  1. ZT 二叉树的非递归遍历

    ZT 二叉树的非递归遍历 二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就 是递归定 ...

  2. K:二叉树的非递归遍历

    相关介绍:  二叉树的三种遍历方式(先序遍历,中序遍历,后序遍历)的非递归实现,虽然递归方式的实现较为简单且易于理解,但是由于递归方式的实现受其递归调用栈的深度的限制,当递归调用的深度超过限制的时候, ...

  3. 二叉树的非递归遍历C++实现

    #include<iostream> #include<stdlib.h> #define maxsize 100 using namespace std; typedef s ...

  4. C++编程练习(17)----“二叉树非递归遍历的实现“

    二叉树的非递归遍历 最近看书上说道要掌握二叉树遍历的6种编写方式,之前只用递归方式编写过,这次就用非递归方式编写试一试. C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历 ...

  5. 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java

    前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...

  6. 二叉树3种递归和非递归遍历(Java)

    import java.util.Stack; //二叉树3种递归和非递归遍历(Java) public class Traverse { /******************一二进制树的定义*** ...

  7. c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)

    二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...

  8. JAVA递归、非递归遍历二叉树(转)

    原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...

  9. 非递归遍历二叉树Java实现

    2018-10-03 20:16:53 非递归遍历二叉树是使用堆栈来进行保存,个人推荐使用双while结构,完全按照遍历顺序来进行堆栈的操作,当然在前序和后序的遍历过程中还有其他的压栈流程. 一.Bi ...

随机推荐

  1. js实现div转图片并保存

    最近工作中遇到的需求,将div转成图片并保存. 1.准备需要用到的js插件jquery-1.8.2.js,html2canvas.min.js(将div转换为canvas),bluebird.js(用 ...

  2. 三调数据库标注插件v1.3

    三调数据库标注插件 插件介绍: 本插件基于VS2010+ARCGIS Addin 开发,高效率处理三调数据,可以标注相关属性.检查尖角.检查节点平均密度.检查地类图斑属性一致性,方便数据导入建库软件之 ...

  3. 关于微信小程序中的样式使用变量值的方法

    在开发过程中,通常碰到样式非固定的情况,这时候就要使用变量来规定样式,例如,一个view的宽度需要使用变量: 1. 在wxss中,定义变量:width:var(--width--); 2. 在js中, ...

  4. (备忘)Linux mount(挂载命令)详解

    挂接命令(mount) 首先,介绍一下挂接(mount)命令的使用方法,mount命令参数非常多,这里主要讲一下今天我们要用到的. 命令格式:mount [-t vfstype] [-o option ...

  5. OpenLDAP 搭建入门

    系统环境:CentOS 7 slapd版本:2.4.44 简介 OpenLDAP是一款轻量级目录访问协议,基于X.500标准的,支持TCP/IP协议,用于实现账号集中管理的开源软件,提供一整套安全的账 ...

  6. IDEA中使用git合并分支的过程报错:cant checkout because of unmerged files

    使用idea的git插件控制代码分支合并时,由于操作不当,报错了,控制台报错如下: cant checkout because of unmerged files,you have to resolv ...

  7. FirstWriting

    在很久很久以前,你拥有我我拥有你 <外面的世界> 在很久很久以前我就有搞一个类似博客的东西的想法,不过一直都没有尝试着搞-- 某天(10号左右吧)刷知乎看到github和hexo可以搭建博 ...

  8. MyBatis-11-一对多处理

    11.一对多处理 比如:一个老师拥有多个学生! 对于老师而言,就是一对多的关系! 环境搭建 环境搭建,和刚才一样 实体类 @Data public class Teacher { private in ...

  9. tsung报告中Transactions Statistics缺失问题

    长时间没有做性能测试,最近在使用tsung进行性能测试时,修改tsung自带的范例脚本后,运行查看结果时,发现测试报告中transactions  statistics缺失,刚开始一直以为是监控中的配 ...

  10. 开放式最短路径优先OSPF

    1.OSPF基本知识 OSPF作为基于链路状态的协议,解决了RIP在收敛慢,路由环路,可扩展性差等问题,还有以下优点: 采用组播方式发布报文,可以减少对其他不运行ospf路由器的影响 ospf直尺无类 ...