二叉树的遍历

来源:互联网 发布:淘宝流量和访客怎么刷 编辑:程序博客网 时间:2024/05/21 18:25

深度优先遍历

1、先序遍历

  1. 访问根节点
  2. 先序遍历左子树
  3. 先序遍历右子树

(1)递归算法实现:

template<class T>void BinaryTree<T>::PreOrder (BinaryTreeNode<T> *root)  {          // 前序周游二叉树或其子树     if (root != NULL) {        Visit(root->value());       // 访问当前结点        PreOrder(root->leftchild());    // 前序周游左子树        PreOrder(root->rightchild());   // 前序周游右子树      }}

递归算法优点:形式简洁,可读性好,正确性容易得到证明,可以给程序的编制和调试带来很大的方便。
递归算法缺点:递归调用比非递归调用消耗的时间与存储空间多,运行的效率较低。

  • 所有的递归算法都可转化成相应的非递归算法。
  • 将递归算法改成相应的非递归算法需要一个来记录调用返回的路径。通过分析递归调用的执行过程,并观察栈的变化就可得到相应的非递归算法。

(2)非递归算法实现:

  1. 根结点进栈
  2. 结点出栈,被访问
  3. 结点的右、左儿子(非空)进栈
  4. 反复执行 2、3 ,至栈空为止
template<class T>void BinaryTree<T>::PreOrderWithoutRecursion(BinaryTreeNode<T> *root)  {    using std::stack;        // 使用STL中的栈    stack<BinaryTreeNode<T>* > aStack;    BinaryTreeNode<T> *pointer = root;        aStack.push(pointer);                       while (!aStack.empty()) {                  pointer=aStack.top();      // 获得栈顶元素                aStack.pop();              // 栈顶元素退栈                Visit(pointer->value());   // 访问当前结点                if (pointer->rightchild() != NULL)        // 非空右孩子入栈                       aStack.push(pointer->rightchild());                if (pointer->leftchild() != NULL)         // 非空左孩子入栈                       aStack.push(pointer->leftchild ()) ;                 }}

2、中序遍历

(1)递归算法实现:

template<class T>void BinaryTree<T>:: InOrder (BinaryTreeNode<T> *root)  {           // 中序周游二叉树或其子树      if (root != NULL) {        InOrder (root->leftchild());    // 中序周游左子树        Visit(root->value());           // 访问当前结点        InOrder(root->rightchild());    // 中序周游右子树       }}

将二叉树严格地按左子树的所有结点位于根结点的左侧,右子树的所有结点位于根结点的右侧的形式绘制,则对每一个结点做一条垂线,映射到水平线上,由此可得该二叉树的中序遍历结果。

中序遍历

(2)非递归算法实现:

  1. 每遇到一个结点就把它推入栈,然后去周游其左子树
  2. 周游完左子树后,从栈顶托出这个结点并访问之
  3. 然后按照其右链接指示的地址再去周游该结点的右子树
template<class T>void BinaryTree<T>::InOrderWithoutRecursion(BinaryTreeNode<T> *root) {    using std::stack;     // 使用STL中的栈    stack<BinaryTreeNode<T>* > aStack;    BinaryTreeNode<T> *pointer = root;    while (!aStack.empty() || pointer) {        if (pointer) {           aStack.push(pointer);              // 当前指针入栈           pointer = pointer->leftchild();    // 左路下降          }else {  // 左子树访问完毕,转向访问右子树           pointer = aStack.top();      // 获得栈顶元素           aStack.pop();                // 栈顶元素退栈           Visit(pointer->value());     // 访问当前结点           pointer = pointer->rightchild();           // 指针指向右孩子        }   }} 

3、后序遍历

(1)递归算法实现:

template<class T>void BinaryTree<T>:: PostOrder (BinaryTreeNode<T> *root)  {           // 后序周游二叉树或其子树    if (root != NULL) {          PostOrder(root->leftchild());     // 后序周游左子树          PostOrder (root->rightchild());   // 后序周游右子树          Visit(root->value());             // 访问当前结点     }}

(2)非递归算法实现:

  1. 每遇到一个结点,先把它推入栈中,去周游它的左子树
  2. 周游完它的左子树后,应继续周游该结点的右子树
  3. 游完右子树之后,才从栈顶托出该结点并访问它

解决方案:

  1. 由于访问某个结点前需要知道是否已经访问该结点的右子树,因此需要给栈中的每个元素加一个标志位tag
  2. 标志位用枚举类型Tags表示,tag为Left表示已进入该结点的左子树;tag为Right表示已进入该结点的右子树
enum Tags{Left,Right};           // 定义枚举类型标志位template <class T>class StackElement  {            // 栈元素的定义public:    BinaryTreeNode<T>* pointer;  // 指向二叉树结点的指针    Tags tag;                    // 标志位};template<class T>void BinaryTree<T>::PostOrderWithoutRecursion(BinaryTreeNode<T>* root)  {    using std::stack;           // 使用STL的栈    StackElement<T> element;    stack<StackElement<T > > aStack;    BinaryTreeNode<T>* pointer;    if (root == NULL)           // 如果是空树则返回        return;     else pointer = root;    while (!aStack.empty() || pointer) {        //如果当前指针非空则压栈并下降到最左子结点        while (pointer != NULL) {                                element.pointer = pointer;                   element.tag = Left;         // 置标志位为Left, 表示进入左子树                   aStack.push(element);                   pointer = pointer->leftchild();        }        element = aStack.top();   // 获得栈顶元素        aStack.pop();             // 栈顶元素退栈        pointer = element.pointer;        if (element.tag == Left) {  // 如果从左子树回来            element.tag = Right;    // 置标志位为Right, 表示进入右子树            aStack.push(element);            pointer = pointer->rightchild();        }else {                          // 如果从右子树回来            Visit(pointer->value());     // 访问当前结点            pointer = NULL;              // 置point指针为空,以继续弹栈        }    }}   

深度优先遍历复杂度分析:

  • 对于有n个结点的二叉树,周游完树的每一个元素都需要O(n)时间。只要对每个结点的处理(函数Visit的执行)时间是一个常数,那么周游二叉树就可以在线性时间内完成。
  • 所需要的辅助空间为周游过程中栈的最大容量,即树的高度。最坏情况下具有n个结点的二叉树高度为n,则所需要的空间复杂度为O(n)

广度优先遍历

  1. 首先访问第0层,也就是根结点所在的层
  2. 然后从左到右依次访问第一层两个结点
  3. 以此类推,当第i层的所有结点访问完之后,再从左至右依次访问第i+1层的各个结点

队列实现广度优先遍历:

template<class T>void BinaryTree<T>::LevelOrder (BinaryTreeNode<T> *root){    using std::queue;          // 使用STL的队列    queue<BinaryTreeNode<T>*> aQueue;    BinaryTreeNode<T> *pointer = root;    if (pointer)        aQueue.push(pointer);       // 根结点入队列    while (!aQueue.empty()) {       // 队列非空        pointer = aQueue.front();  // 获得队列首结点        aQueue.pop();              // 当前结点出队列        Visit(pointer->value());   // 访问当前结点        if (pointer->leftchild() != NULL)            aQueue.push(pointer->leftchild());    // 左子树进队列        if (pointer->rightchild()!= NULL)            aQueue.push(pointer->rightchild());  // 右子树进队列    }}
0 0
原创粉丝点击