二叉树的遍历方式与线索化实现

目录

1、二叉树结构的层次遍历 

2、二叉树的典型遍历方式 (先序、中序、后序)

3、二叉树的线索化实现


1、二叉树结构的层次遍历 

二叉树的遍历 

       二叉树的遍历(Traversing Binay Tree)是指从根结点出发按照某种次序

       依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次

 

                通用树结构的层次遍历算法可以用在二叉树结构

设计思路(游标) 

                提供一组遍历相关的函数,按层次访问二叉树中的数据元素。 

        

层次遍历算法 

      -原料: class LinkQueue<T>; 

      -游标: LinkQueue<T>: :front(); 

      -思想

                ● begin() → 将根结点压入队列中 

                ● current() → 访问队头元素指向的数据元素

                ● next() → 队头元素弹出,将队头元素的孩子压入队列中核心) 

                ● end() → 判断队列是否为空 

层次遍历算法示例 

            

 

2、编程实验 

由于通用树结构(GTree)和 二叉树(BTree)都需要实现一组遍历相关函数所以在抽象父类Tree中设置一组纯虚函数

Tree.h新增:

virtual bool begin() = 0;  
virtual bool end() = 0;  
virtual bool next() = 0;  
virtual T current() = 0;  

BTree.h新增:

bool begin()  
{  
    bool ret = (root() != NULL);  
  
    if(ret)  
    {  
        m_queue.clear();  
        m_queue.add(root());  
    }  
  
    return ret;  
}  
  
bool end()  
{  
    return (m_queue.length() == 0);  
}  
  
bool next()  
{  
    bool ret = (m_queue.length() > 0);  
  
    if( ret )  
    {  
        BTreeNode<T>* node = m_queue.front();   // node指向队头  
  
        m_queue.remove();   // 游标移动指向下一个结点  
  
        if(node->left != NULL)  
        {  
            m_queue.add(node->left);  
        }  
  
        if(node->right != NULL)  
        {  
            m_queue.add(node->right);  
        }  
  
    }  
  
    return ret;  
}  
  
T current()  
{  
    if( !end() )  
    {  
        return m_queue.front()->value;  
    }  
    else  
    {  
        THROW_EXCEPTION(InvalidOperationException, "No value in current position ...");  
    }  
} 

 为了安全性所以我们在删除结点后执行清空队列操作

void clear()  
{  
    free(root());  
  
    m_queue.clear();  
  
    this->m_root = NULL;  
} 
SharedPointer< Tree<T> > remove(const T& value)  
 {  
     BTree<T>* ret = NULL;  
  
     BTreeNode<T>* node = find(value);  
  
     if(node == NULL)  
     {  
         THROW_EXCEPTION(InvalidParameterException,"Can not find the tree node via value ...");  
     }  
     else  
     {  
         remove(node,ret);  
         m_queue.clear();  
     }  
  
     return ret;  
 }  
  
 SharedPointer< Tree<T> > remove(TreeNode<T>* node)  
 {  
     BTree<T>* ret = NULL;  
  
     node = find(node);  
  
     if(node == NULL)  
     {  
         THROW_EXCEPTION(InvalidParameterException,"Parameter node is invalid ...");  
     }  
     else  
     {  
         remove(dynamic_cast<BTreeNode<T>*>(node), ret);  
         m_queue.clear();  
     }  
  
     return ret;  
 } 

 main.cpp

#include <iostream>
#include "BTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    BTree<int> bt;
    BTreeNode<int>* n = NULL;

    bt.insert(1,NULL);

    n = bt.find(1);
    bt.insert(2, n);
    bt.insert(3, n);


    n = bt.find(2);
    bt.insert(4, n);
    bt.insert(5, n);

    n = bt.find(4);
    bt.insert(8, n);
    bt.insert(9, n);

    n = bt.find(5);
    bt.insert(10, n);

    n = bt.find(3);
    bt.insert(6, n);
    bt.insert(7, n);

    cout << bt.count() << endl;
    cout << bt.height() << endl;
    cout << bt.degree() << endl;

    for(bt.begin(); !bt.end(); bt.next())
    {
        cout << bt.current() << " ";
    }

    return 0;
}

  


2、二叉树的典型遍历方式 (先序、中序、后序)

典型的二叉树遍历方式 

    -先序遍历 ( Pre-Order Traversal ) 

    -中序遍历 ( In-Order Traversal )

    -后序遍历 ( Post-Order Traversal ) 

先序遍历( Pre-Order Traversal ) 

    -二叉树为空: 无操作,直接返回 

    -二叉树不为空: 

             ① 访问根结点中的数据元素 

             ② 先序遍历左子树 

             ③ 先序遍历右子树 

                     

中序遍历( In-Order Traversal ) 

    -二叉树为空: 无操作,直接返回 

    -二叉树不为空:

             ① 中序遍历左子树 

             ② 访问根结点中的数据元素 

             ③ 中序遍历右子树

                       

后序遍历( Post-Order Traversal ) 

    -二叉树为空: 无操作,直接返回 

    -二叉树不为空: 

             ① 后序遍历左子树 

             ② 后序遍历右子树 

             ③ 访问根结点中的数据元素

                       

是否可以将二叉树的典型遍历算法集成到BTree中?

如果可以,代码需要做怎样的改动?

设计要点 

       -不能与层次遍历函数冲突必须设计新的函数接口 

       -算法执行完成后能够方便的获得遍历结果 

       -遍历结果能够反映结点访问的先后次序 

函数接口设计 

       -SharedPointer< Array<T> > traversal(BTTraversal order

                 ● 根据参数 order 选择执行遍历算法(先序,中序,后序) 

                 ● 返回值为堆中的数组对象(生命期由智能指针管理) 

                 ● 数组元素的次序反映遍历的先后次序 

enum BTTraversal
{
    PreOrder,    // 先序遍历
    InOrder,     // 中序遍历
    PostOrder,   // 后序遍历
    LeverOrder   // 层次遍历
};

典型遍历使用示例

    SharedPointer< Array<int> > sp = NULL;

    sp = bt.traversal(PreOrder); // 先序遍历

    for(int i=0; i<(*sp).length(); i++)
 
    {
        cout << (*sp)[i] << " "; 
    }

    层次遍历算法示例

             

            

编程实验 

二叉树的典遍历方式 

#ifndef BTREE_H
#define BTREE_H

#include "Tree.h"
#include "BTreeNode.h"
#include "Exception.h"
#include "LinkQueue.h"
#include "DynamicArray.h"

namespace DTLib
{

enum BTTraversal
{
    PreOrder,
    InOrder,
    PostOrder,
    LeverOrder
};

template < typename T >
class BTree : public Tree<T>
{
protected:
    // ...

    void preOrderTraversal(BTreeNode<T>* node, LinkQueue<BTreeNode<T>*>& queue) 
    {
        if(node != NULL)
        {
            queue.add(node);
            preOrderTraversal(node->left, queue);
            preOrderTraversal(node->right, queue);
        }
    }

    void inOrderTraversal(BTreeNode<T>* node,LinkQueue<BTreeNode<T>*>& queue)
    {
        if(node != NULL)
        {        
            inOrderTraversal(node->left, queue);
            queue.add(node);
            inOrderTraversal(node->right, queue);
        }
    }

    void postOrderTraversal(BTreeNode<T>* node, LinkQueue<BTreeNode<T>*>& queue)
    {
        if(node != NULL)
        {            
            postOrderTraversal(node->left, queue);
            postOrderTraversal(node->right, queue);
            queue.add(node);
        }
    }

    void leverOrderTraversal(BTreeNode<T>* node, LinkQueue<BTreeNode<T>*>& queue)
    {
        if(node != NULL)
        {
            LinkQueue<BTreeNode<T>*> tmp;

            tmp.add(node);

            while(tmp.length() > 0)
            {
                BTreeNode<T>* n = tmp.front();

                if(n->left != NULL)
                {
                    tmp.add(n->left);
                }

                if(n->right != NULL)
                {
                    tmp.add(n->right);
                }

                tmp.remove();

                queue.add(n);
            }
        }
    }

    void traversal(BTTraversal order, LinkQueue<BTreeNode<T>*>& queue)
    {
        switch( order )
        {
            case PreOrder:
                preOrderTraversal(root(), queue);
                break;
            case InOrder:
                inOrderTraversal(root(), queue);
                break;
            case PostOrder:
                postOrderTraversal(root(), queue);
                break;
            case LeverOrder:
                leverOrderTraversal(root(), queue);
                break;
            default:
                THROW_EXCEPTION(InvalidParameterException, "Parameter order is invalid ...");
        }
    }

public:
    // ...

    SharedPointer< Array<T> > traversal(BTTraversal order)
    {
        DynamicArray<T>* ret = NULL;
        LinkQueue<BTreeNode<T>*> queue;

        traversal(order, queue); // 遍历,遍历结果保存在queue

        ret = new DynamicArray<T>(queue.length());

        if(ret != NULL)
        {
            /* 将队列里结点数据元素值依次放到返回数组里 */
            for(int i=0; i<ret->length(); i++, queue.remove())
            {
                ret->set(i, queue.front()->value);
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create return array ...");
        }

        return ret;
    }

   // ...
};
}
#endif // BTREE_H

main.cpp

#include <iostream>
#include "BTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    BTree<int> bt;
    BTreeNode<int>* n = NULL;

    bt.insert(1,NULL);

    n = bt.find(1);
    bt.insert(2, n);
    bt.insert(3, n);


    n = bt.find(2);
    bt.insert(4, n);
    bt.insert(5, n);

    n = bt.find(4);
    bt.insert(8, n);
    bt.insert(9, n);

    n = bt.find(5);
    bt.insert(10, n);

    n = bt.find(3);
    bt.insert(6, n);
    bt.insert(7, n);

    SharedPointer< Array<int> > sp = NULL;

    sp = bt.traversal(PreOrder);

    for(int i=0; i<(*sp).length(); i++)

    {
        cout << (*sp)[i] << " ";
    }

    cout << endl;

    return 0;
}

   

小结 

            二叉树的典型遍历都是以递归方式执行的 

            BTree以不同的函数接口支持典型遍历 

            层次遍历与典型遍历互不冲突 

            遍历结果能够反映树结点访问的先后次序 

 

3、二叉树的线索化实现

什么是线索化二叉树? 

   -将二叉树转换为双向链表的过程(非线性 → 线性

   -能够反映某种二叉树的遍历次序(结点的先后访问次序

           ● 利用结点的 right指针指向遍历中的后继结点 

           ● 利用结点的 left指针指向遍历中的前驱结点 

如何对二叉树进行线索化? 

        思维过程: 

        

二叉树的线索化 

            

函数接口设计 

   -BTreeNode<T>* thread(BTTraversal order) 

            ● 根据参数 order 选择线索化的次序(先序,中序,后序,层次) 

            ● 返回值线索化之后指向链表首结点的指针 

            ● 线索化执行结束之后对应的二叉树变为空树 

线索化流程

                        

 

队列中结点的连接算法[ connect(queue)

 

编程实验 

二叉树的线索化     thread (order)

template < typename T >
class BTree : public Tree<T>
{
protected:
    // ...

    BTreeNode<T>* connect(LinkQueue<BTreeNode<T>*>& queue)
    {
        BTreeNode<T>* ret = NULL;

        if(queue.length() > 0)
        {
            ret = queue.front();

            BTreeNode<T>* slider = queue.front();

            queue.remove();

            slider->left = NULL;

            while(queue.length() > 0)
            {
                slider->right = queue.front();
                queue.front()->left = slider;
                slider = queue.front();
                queue.remove();
            }

            slider->right = NULL;   // 最后一个结点next指针为空
        }

        return ret;
    }

public:
    // ...

    BTreeNode<T>* thread(BTTraversal order)
    {
        BTreeNode<T>* ret = NULL;
        LinkQueue< BTreeNode<T>* > queue;

        traversal(order, queue); // 遍历二叉树,将结果保存在queue队列

        ret = connect(queue);    //  将队列里已遍历好的有次序的结点连成双向链表

        this->m_root = NULL;     // 置为空树
 
        m_queue.clear();

        return ret;

    }
};

main.cpp

#include <iostream>
#include "BTree.h"

using namespace std;
using namespace DTLib;

int main()
{
    BTree<int> bt;
    BTreeNode<int>* n = NULL;

    bt.insert(1, NULL);

    n = bt.find(1);
    bt.insert(2, n);
    bt.insert(3, n);


    n = bt.find(2);
    bt.insert(4, n);
    bt.insert(5, n);
 
    n = bt.find(4);
    bt.insert(8, n);
    bt.insert(9, n);

    n = bt.find(5);
    bt.insert(10, n);

    n = bt.find(3);
    bt.insert(6, n);
    bt.insert(7, n);


    cout << "Old BTree: " << endl;

    for(bt.begin(); !bt.end(); bt.next())
    {
        cout << bt.current() << " ";
    }

    cout << endl << endl;


    BTreeNode<int>* head = bt.thread(LeverOrder);

    while( head->right )
    {
        head = head->right;
    }

    while( head )
    {
        cout << head->value << " ";

        head = head->left;
    }


    return 0;
}

    

 

小结 

            线索化是将二叉树转换为双向链表的过程 

            线索化之后结点间的先后次序符合某种遍历次序 

            线索化操作将破坏原二叉树结点间的父子关系 

            线索化之后二叉树将不再管理结点的生命期 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值