二叉树的遍历

深度优先遍历

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值