二叉树的深度优先遍历、广度优先遍历和非递归遍历
二叉树的遍历:
D:访问根结点,L:遍历根结点的左子树,R:遍历根结点的右子树。
给定一棵二叉树的前序遍历序列和中序遍历序列可以惟一确定一棵二叉树。
二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法是采用队列。
深度优先遍历二叉树。
1. 中序遍历(LDR)的递归算法:
若二叉树为空,则算法结束;否则:
中序遍历根结点的左子树;
访问根结点;
中序遍历根结点的右子树。
2. 前序遍历(DLR)的递归算法:
若二叉树为空,则算法结束,否则:
访问根结点;
前序遍历根结点的左子树;
前序遍历根结点的右子树。
3. 后序遍历(LRD)的递归算法:
若二叉树为空,则算法结束,否则:
后序遍历根结点的左子树;
后序遍历根结点的右子树;
访问根结点。
广度优先遍历二叉树。
广度优先周游二叉树(层序遍历)是用队列来实现的,从二叉树的第一层(根结点)开始,自上至下逐层遍历;在同一层中,按照从左到右的顺序对结点逐一访问。
按照从根结点至叶结点、从左子树至右子树的次序访问二叉树的结点。算法:
1初始化一个队列,并把根结点入列队;
2当队列为非空时,循环执行步骤3到步骤5,否则执行6;
3出队列取得一个结点,访问该结点;
4若该结点的左子树为非空,则将该结点的左子树入队列;
5若该结点的右子树为非空,则将该结点的右子树入队列;
6结束。
非递归深度优先遍历二叉树。
栈是实现递归的最常用的结构,利用一个栈来记下尚待遍历的结点或子树,以备以后访问,可以将递归的深度优先遍历改为非递归的算法。
1. 非递归前序遍历:遇到一个结点,就访问该结点,并把此结点推入栈中,然后下降去遍历它的左子树。遍历完它的左子树后,从栈顶托出这个结点,并按照它的右链接指示的地址再去遍历该结点的右子树结构。
2. 非递归中序遍历:遇到一个结点,就把它推入栈中,并去遍历它的左子树。遍历完左子树后,从栈顶托出这个结点并访问之,然后按照它的右链接指示的地址再去遍历该结点的右子树。
3. 非递归后序遍历:遇到一个结点,把它推入栈中,遍历它的左子树。遍历结束后,还不能马上访问处于栈顶的该结点,而是要再按照它的右链接结构指示的地址去遍历该结点的右子树。遍历遍右子树后才能从栈顶托出该结点并访问之。另外,需要给栈中的每个元素加上一个特征位,以便当从栈顶托出一个结点时区别是从栈顶元素左边回来的(则要继续遍历右子树),还是从右边回来的(该结点的左、右子树均已周游)。特征为Left表示已进入该结点的左子树,将从左边回来;特征为Right表示已进入该结点的右子树,将从右边回来。
4. 简洁的非递归前序遍历:遇到一个结点,就访问该结点,并把此结点的非空右结点推入栈中,然后下降去遍历它的左子树。遍历完左子树后,从栈顶托出一个结点,并按照它的右链接指示的地址再去遍历该结点的右子树结构。
附代码:BinTree.h
/**//******************************************************************** @created: 2011/09/26 晚 网吧 @author: weedge E-mail:[email protected] @comment: 1.二叉树的3种深度遍历方式(包括非递归实现),前序,后序和中序。 2.二叉树的广度遍历。 3.已知中序和后序遍历 建立一棵二叉树;已知中序和前序遍历 建立一棵二叉树. ***************************************************************************************/ #ifndef BIN_TREE_H #define BIN_TREE_H #include<iostream> #include<stack> #include<queue> #include<cstring> #include<cassert> using namespace std; typedef int ElemType; typedef struct treeT { ElemType key; struct treeT* left; struct treeT* right; }treeT, *pTreeT; /*insert*/ pTreeT BT_Insert(ElemType target, pTreeT* ppTree); /*已知中序和后序遍历 建立一棵二叉树*/ void TreeFromMidPost(pTreeT *ppTree, string mid, string post, int lm, int rm, int lp, int rp); /*已知中序和前序遍历 建立一棵二叉树*/ void TreeFromMidPre(pTreeT *ppTree, string mid, string pre, int lm, int rm, int lp, int rp); /*深度遍历*/ void BT_PreOrder(pTreeT root); void BT_PreOrderNoRec(pTreeT root); void BT_InOrder(pTreeT root); void BT_InOrderNoRec(pTreeT root); void BT_PostOrder(pTreeT root); void BT_PostOrderNoRec(pTreeT root); /*广度遍历*/ void BT_LevelOrder(pTreeT root); #endif
BinTree.cpp
#include "BinTree.h" /**//*=========================================================================== * Function name: visit * Parameter: root:树根节点指针 * Precondition: * Description: * Return value: * Author: Liu Qi, //- ===========================================================================*/ static void visit(pTreeT root) { if (NULL != root) { printf(" %d\n", root->key); } } /**//*=========================================================================== * Function name: BT_MakeNode * Parameter: target:元素值 * Precondition: None * Postcondition: NULL != pTreeT * Description: 构造一个tree节点,置左右指针为空,并且返回指向新节点的指针 * Return value: 指向新节点的指针 * Author: Liu Qi, [12/30/2005] ===========================================================================*/ static pTreeT BT_MakeNode(ElemType target) { pTreeT pNode = (pTreeT) malloc(sizeof(treeT)); assert( NULL != pNode ); pNode->key = target; pNode->left = NULL; pNode->right = NULL; return pNode; } /**//*=========================================================================== * Function name: BT_Insert * Parameter: target:要插入的元素值, pNode:指向某一个节点的指针 * Precondition: NULL != ppTree * Description: 插入target到pNode的后面 * Return value: 指向新节点的指针 * Author: Liu Qi, [12/29/2005] ===========================================================================*/ pTreeT BT_Insert(ElemType target, pTreeT *ppTree) { pTreeT Node; assert( NULL != ppTree ); Node = *ppTree; if (NULL == Node) { return *ppTree = BT_MakeNode(target); } if (Node->key == target) //不允许出现相同的元素 { return NULL; } else if (Node->key > target) //向左 { return BT_Insert(target, &Node->left); } else { return BT_Insert(target, &Node->right); } } /*================================================================ //函数名称:TreeFromMidPost //函数功能:给出一棵二叉树的中序与后序序列,构造这棵二叉树。 //输入参数:pTreeT *ppTree:二叉树的结点 // string mid:存储了二叉树的中序序列的字符串 // string post:存储了二叉树的后序序列的字符串 // int lm, int rm:二叉树的中序序列在数组mid中的左右边界 // int lp, int rp:二叉树的后序序列在数组post中的左右边界 ========================================================================*/ void TreeFromMidPost(pTreeT *ppTree, string mid, string post, int lm, int rm, int lp, int rp) { //构造二叉树结点 *ppTree = BT_MakeNode(post[rp]); int pos = lm; while (mid[pos] != post[rp]) { pos++; } int iLeftChildLen = pos - lm; if (pos > lm)//有左孩子,递归构造左子树 { TreeFromMidPost(&((*ppTree)->left), mid, post, lm, pos - 1, lp, lp + iLeftChildLen - 1); } if (pos < rm)//有右孩子,递归构造右子树 { TreeFromMidPost(&((*ppTree)->right), mid, post, pos + 1, rm, lp + iLeftChildLen, rp - 1); } } /*================================================================ //函数名称:TreeFromMidPre //函数功能:给出一棵二叉树的先序与中序序列,构造这棵二叉树。 //输入参数: pTreeT *ppTree:二叉树的结点 // string mid:存储了二叉树的中序序列的字符串 // string pre:存储了二叉树的先序序列的字符串 // int lm, int rm:二叉树的中序序列在数组mid中的左右边界 // int lp, int rp:二叉树的先序序列在数组pre中的左右边界 ================================================================*/ void TreeFromMidPre(pTreeT *ppTree, string mid, string pre, int lm, int rm, int lp, int rp) { //构造二叉树结点 *ppTree = BT_MakeNode(pre[lp]); int pos = lm; while (mid[pos] != pre[lp]) { pos++; } int iLeftChildLen = pos - lm; if (pos > lm)//有左孩子,递归构造左子树 { TreeFromMidPre(&((*ppTree)->left), mid, pre, lm, pos - 1, lp + 1, lp + iLeftChildLen); } if (pos < rm)//有右孩子,递归构造右子树 { TreeFromMidPre(&((*ppTree)->right), mid, pre, pos + 1, rm, lp + iLeftChildLen + 1, rp); } } /**//*=========================================================================== * Function name: BT_PreOrder * Parameter: root:树根节点指针 * Precondition: None * Description: 前序遍历 * Return value: void * Author: Liu Qi, [12/29/2005] ===========================================================================*/ void BT_PreOrder(pTreeT root) { if (NULL != root) { visit(root); BT_PreOrder(root->left); BT_PreOrder(root->right); } } /**//*=========================================================================== * Function name: BT_PreOrderNoRec * Parameter: root:树根节点指针 * Precondition: Node * Description: 前序(先根)遍历非递归算法 * Return value: void * Author: Liu Qi, [1/1/2006] ===========================================================================*/ void BT_PreOrderNoRec(pTreeT root) { stack<treeT *> s; while ((NULL != root) || !s.empty()) { if (NULL != root) { visit(root); s.push(root); root = root->left; } else { root = s.top(); s.pop(); root = root->right; } } } /**//*=========================================================================== * Function name: BT_InOrder * Parameter: root:树根节点指针 * Precondition: None * Description: 中序遍历 * Return value: void * Author: Liu Qi, [12/30/2005] ===========================================================================*/ void BT_InOrder(pTreeT root) { if (NULL != root) { BT_InOrder(root->left); visit(root); BT_InOrder(root->right); } } /**//*=========================================================================== * Function name: BT_InOrderNoRec * Parameter: root:树根节点指针 * Precondition: None * Description: 中序遍历,非递归算法 * Return value: void * Author: Liu Qi, [1/1/2006] ===========================================================================*/ void BT_InOrderNoRec(pTreeT root) { stack<treeT *> s; while ((NULL != root) || !s.empty()) { if (NULL != root) { s.push(root); root = root->left; } else { root = s.top(); visit(root); s.pop(); root = root->right; } } } /**//*=========================================================================== * Function name: BT_PostOrder * Parameter: root:树根节点指针 * Precondition: None * Description: 后序遍历 * Return value: void * Author: Liu Qi, [12/30/2005] ===========================================================================*/ void BT_PostOrder(pTreeT root) { if (NULL != root) { BT_PostOrder(root->left); BT_PostOrder(root->right); visit(root); } } /**//*=========================================================================== * Function name: BT_PostOrderNoRec * Parameter: root:树根节点指针 * Precondition: None * Description: 后序遍历,非递归算法 * Return value: void * Author: weege, [09/26/2011] ===========================================================================*/ void BT_PostOrderNoRec(pTreeT root) { stack<treeT *> s; treeT *p = root; treeT *pre = NULL;//pre表示最近一次访问的结点 while(p!=NULL || s.size()!=0) { while(p) { s.push(p); p = p->left; } p = s.top(); //如果p没有右孩子或者其右孩子刚刚被访问过 if(p->right == NULL || p->right == pre) {//visit this element and then pop it cout << "visit: " << p->key << endl; s.pop(); pre = p; p = NULL; }else{ p = p->right; } } } /**//*=========================================================================== * Function name: BT_LevelOrder * Parameter: root:树根节点指针 * Precondition: NULL != root * Description: 层序遍历 * Return value: void * Author: Liu Qi, [1/1/2006] ===========================================================================*/ void BT_LevelOrder(pTreeT root) { queue<treeT *> q; treeT *treePtr; assert( NULL != root ); q.push(root); while (!q.empty()) { treePtr = q.front(); q.pop(); visit(treePtr); if (NULL != treePtr->left) { q.push(treePtr->left); } if (NULL != treePtr->right) { q.push(treePtr->right); } } }
test.cpp
#include<iostream> #include <ctime> #include "BinTree.h" using namespace std; #define MAX_CNT 5 #define BASE 100 int main(int argc, char *argv[]) { int i; pTreeT root = NULL; srand( (unsigned)time( NULL ) ); for (i=0; i<MAX_CNT; i++) { BT_Insert(rand() % BASE, &root); } //前序 printf("PreOrder:\n"); BT_PreOrder(root); printf("\n"); printf("PreOrder no recursion:\n"); BT_PreOrderNoRec(root); printf("\n"); //中序 printf("InOrder:\n"); BT_InOrder(root); printf("\n"); printf("InOrder no recursion:\n"); BT_InOrderNoRec(root); printf("\n"); //后序 printf("PostOrder:\n"); BT_PostOrder(root); printf("\n"); printf("PostOrder no recursion:\n"); BT_PostOrderNoRec(root); printf("\n"); //层序 printf("LevelOrder:\n"); BT_LevelOrder(root); printf("\n"); system("pause"); return 0; }
----------------------------------------------------------------------
图的深度优先搜索法是树的先根遍历的推广,它的基本思想是:从图G的某个顶点v0出发,访问v0,然后选择一个与v0相邻且没被访问过的顶点vi访问,再从vi出发选择一个与vi相邻且未被访问的顶点vj进行访问,依次继续。如果当前被访问过的顶点的所有邻接顶点都已被访问,则退回到已被访问的顶点序列中最后一个拥有未被访问的相邻顶点的顶点w,从w出发按同样的方法向前遍历,直到图中所有顶点都被访问。
图的广度优先搜索是树的按层次遍历的推广,它的基本思想是:首先访问初始点vi,并将其标记为已访问过,接着访问vi的所有未被访问过的邻接点vi1,vi2, …, vi t,并均标记已访问过,然后再按照vi1,vi2, …,vi t的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依次类推,直到图中所有和初始点vi有路径相通的顶点都被访问过为止。详见:http://www.cnblogs.com/baiyanhuang/archive/2011/04/17/1999196.html