主要过程就是递归调用,也可以用栈来实现。
对于先序遍历来说,蓝色剪头第一次经过的结点,就是遍历的序列,以后再次经历就不算进去了。
typedef struct BiNode{ int data;//数据域 BiNode *lchild, *rchild;//左右孩子指针 } BiNode, *BiTree; void preorder(BiNode *root){ if (root != NULL) { //访问根节点 cout << "先序遍历" << root->data; 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; } } }
中序遍历、后序遍历和先序遍历思想基本类似,对于中序遍历来说,蓝色剪头第二次经过的结点,就是遍历的序列,之前的和以后的再次经历就不算进序列里去了。对于后序遍历来说,蓝色剪头第三次经过的结点,就是遍历的序列,之前经历的就不算进去了。
若二叉树非空
(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 }
后序遍历序列: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)