您的位置:首页 > 其它

二叉树的存储方式以及递归和非递归的三种遍历方式

2018-03-14 10:17 387 查看
主要过程就是递归调用,也可以用栈来实现。

对于先序遍历来说,蓝色剪头第一次经过的结点,就是遍历的序列,以后再次经历就不算进去了。

typedef struct BiNode{
int data;//数据域
BiNode *lchild, *rchild;//左右孩子指针
} BiNode, *BiTree;

void preorder(BiNode *root){
if (root != NULL) {
//访问根节点
cout << "先序遍历" << root->d
4000
ata;
preorder(root->lchild);
preorder(root->rchild);
}// end of if
}


非递归的先序遍历根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:对于任一结点P:     1)访问结点P,并将结点P入栈;     2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;     3)直到P为NULL并且栈为空,则遍历结束。

//关键在于何时访问的语句的位置
void preorder(BiTree root){
//初始化栈
stack<BiTree> nodes;
BiNode *p = root;
while (p != NULL || !nodes.empty()) {
while (p != NULL) {
//根左右的顺序遍历
cout << p->data;
//进栈
nodes.push(p);
//继续移动
p = p->lchild;
}
//p == null
if (!nodes.empty()) {
//对 p 重新指向
p = nodes.top();
//出栈
nodes.pop();
//转到右子树
p = p->rchild;
}
}
}


中序遍历、后序遍历和先序遍历思想基本类似,对于中序遍历来说,蓝色剪头第二次经过的结点,就是遍历的序列,之前的和以后的再次经历就不算进序列里去了。对于后序遍历来说,蓝色剪头第三次经过的结点,就是遍历的序列,之前经历的就不算进去了。LDR左跟右:中序遍历左子树、访问根结点、中序遍历右子树若二叉树非空  (1)中序遍历左子树;  (2)访问根结点;  (3)中序遍历右子树;若二叉树为空,结束——基本项(也叫终止项)若二叉树非空——递归项    (1)中序遍历左子树;    (2)访问根结点;    (3)中序遍历右子树;中序递归遍历算法

void inOrder(BiNode *root){
if (root != NULL) {
inOrder(root->lchild);
cout << "先序遍历" << root->data;
inOrder(root->rchild);
}// end of if
}


中序的非递归遍历,使用栈





//非递归的中序遍历二叉树
void inOrder(BiTree root){
//非递归中序遍历(左跟右)
stack<BiTree> nodes;//初始化栈
//指示指针
BiNode *p = root;
//遍历二叉树的循环语句
while (p != NULL || !nodes.empty()) {
while (p != NULL) {
//不为空就入栈
nodes.push(p);
//一直向做走,直到为 kong
p = p->lchild;
}
// 需要判断空否,因为需要出栈操作
if (!nodes.empty()) {
//令 p 重新指向 栈顶结点
p = nodes.top();
//访问根节点(栈顶结点)
cout << p->data << " ";
//使用完毕,弹出
nodes.pop();
//向右遍历
p = p->rchild;
}
}// end of while
}


LRD左右跟:后序遍历左子树、后序遍历右子树、访问根结点

后序遍历序列:BDFGECA

//递归后续遍历二叉树
void lastOrder(BiTree root){
if (root != NULL) {
lastOrder(root->lchild);
lastOrder(root->rchild);
cout << root->data;
}
}


同理有非递归的后续遍历二叉树在后序遍历中,要保证左孩子和右孩子都已被访问,并且左孩子在右孩子访问之后才能访问根结点。因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

void postOrder3(BiTree root)     //非递归后序遍历
{
stack<BiTree> nodes;
//当前结点
BiNode *cur;
//前一次访问的结点
BiNode *pre = NULL;
//根节点入栈
nodes.push(root);
//依次遍历左右子树
while(!nodes.empty())
{
cur = nodes.top();
//判断 cur 结点的左右孩子子树的情况
if((cur->lchild == NULL && cur->rchild == NULL) ||
(pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
{
//如果当前结点没有孩子结点或者孩子节点都已被访问过
cout << cur->data;
//出栈
nodes.pop();
//前一次访问的结点, pre标记已经访问的结点
pre = cur;
}
else
{
//左右跟的访问顺序,关键还是访问语句的位置!!!一定是先写右子树,再写左子树,顺序不能错
//如果当前结点的右子树不为空
if(cur->rchild != NULL){
nodes.push(cur->rchild);
}
//如果当前结点的左子树不为空
if(cur->lchild != NULL){
nodes.push(cur->lchild);
}
}
}
}


二叉树遍历的总结:无论先序、中序、后序遍历二叉树,遍历时的搜索路线是相同的:从根节点出发,逆时针延二叉树外缘移动,对每个节点均途经三次。

先序遍历:第一次经过节点时访问。(ABCD)中序遍历:第二次经过节点时访问。(BADC)后序遍历:第三次经过节点时访问。(BDCA) 辛苦的劳动,转载请注明出处,谢谢……http://www.cnblogs.com/kubixuesheng/p/4378266.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: