Algorithm7 —— 二叉树的各种遍历

本系列算法实现都是在学习数据结构(C++语言版),清华大学邓俊辉教授编,听邓老师edX网课过程中自己实现的例子。

参考链接

  • 二叉树前序、中序、后序遍历非递归写法的透彻解析
  • Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)

主要记录

  1. 二叉树建立
  2. 二叉树删除
  3. 二叉树遍历
    广度优先搜索—-层序遍历法
    前中后序递归遍历法
    前中后序迭代遍历法
    前中后序Morris Traversal遍历法

1 二叉树建立

1.1 二叉树节点BinNode模板类

关键部分, 数据结构的存储, 有四个关键元素

BinNode<T>* m_parent;
BinNode<T>* m_lchild;
BinNode<T>* m_rchild;

T m_data;

具体的BinNode模板类如下

template< typename T >
class BinNode
{
public:
    BinNode* m_parent;
    BinNode* m_lchild;
    BinNode* m_rchild;

    T m_data;
    unsigned int m_height;
    // unsigned int m_deepth;

public:
    BinNode(): m_parent(nullptr) , m_lchild(nullptr), m_rchild(nullptr), m_height(0)
    {
    }
    BinNode(const T& data, BinNode* p = nullptr, BinNode* lc = nullptr, BinNode* rc = nullptr)
        : m_data(data), m_parent(p) , m_lchild(lc), m_rchild(rc), m_height(0)
    {
    }
    ~BinNode()    {}



    //! judgement bintree
    bool isRoot()       { return !this->m_parent;    }
    bool isLchild()     { return !isRoot() && this == this->m_parent->m_lchild;    }
    bool isRchild()     { return !isRoot() && this == this->m_parent->m_rchild;    }
    bool hasParent()    { return !isRoot();    }
    bool hasLchild()    { return this->m_lchild;    }
    bool hasRchild()    { return this->m_rchild;    }
    bool hasChild()     { return hasLchild() || hasRchild();    }
    bool hasBothChild() { return hasLchild() && hasRchild();    }
    bool isLeaf()       { return !hasChild();    }



    BinNode* insertLchild(const T& data)
    {
        if (!hasLchild())
        {
            m_lchild = new BinNode(data, this);
        }
        return m_lchild;
    }
    BinNode* insertRchild(const T& data)
    {
        if (!hasRchild())
        {
            m_rchild = new BinNode(data, this);
        }
        return m_rchild;
    }
};

1.2 二叉树建立

手动建立一个简单的二叉树, 如下图

           A(5)
           / \
        B(3)  C(4)
        / \
      D(1) E(2)
           / \
              F(9)

代码如下

    BinTree bintree;
    BinNode*A, *B, *C, *D, *E, *F;
    A = bintree.insertRoot(vec.at(0));
    B = A->insertLchild(vec.at(1));
    C = A->insertRchild(vec.at(2));
    D = B->insertLchild(vec.at(3));
    E = B->insertRchild(vec.at(4));
    F = E->insertRchild(vec.at(5));

//或者

    BinTree<int>* bintree;
    BinNode<int>*A, *B, *C, *D, *E, *F;
    bintree = new BinTree<int>();
    A = bintree->insertRoot(5);
    B = A->insertLchild(3);
    C = A->insertRchild(4);
    D = B->insertLchild(1);
    E = B->insertRchild(2);
    F = E->insertRchild(9);
    // do something
    delete bintree;

2 二叉树删除

二叉树删除采用后序遍历的思路进行删除, 自下而上, 自左而右.
使用后序递归遍历代码如下

    //! remove node and its childs
    void removeChildTree(BinNode<T>*node)
    {
        if (node->hasLchild())
        {
            removeChildTree(node->m_lchild);
        }
        if (node->hasRchild())
        {
            removeChildTree(node->m_rchild);
        }

        //delete
        if (node)
        {
            std::cout << node->m_data << " ";
            fromParentTo(node) = nullptr; //切断来自父节点的指针
            delete node;
            node = nullptr;
        }
    }

3 二叉树遍历

3.1 广度优先搜索

广度优先搜索算法(Breadth First Search),又叫宽度优先搜索,或横向优先搜索. 在二叉树的遍历中我们也叫层序遍历.
一层一层自上而下, 从左到右的顺序访问节点数据.为了保证先进来的先访问, 也就是先进先出, 使用一个辅助队列queue来实现.

    template <typename VST>
    void traversalLevel(BinNode*node, VST& visit) //use queue
    {
        std::queue*> q;
        if (node)
        {
            q.push(node);
        }

        while (!q.empty())
        {
            BinNode* tmpNode = q.front();
            q.pop();
            visit(tmpNode->m_data);

            if (tmpNode->hasLchild())
            {
                q.push(tmpNode->m_lchild);
            }
            if (tmpNode->hasRchild())
            {
                q.push(tmpNode->m_rchild);
            }
        }
    }

3.2 深度优先搜索

深度优先搜索遍历二叉树有前中后序三种, 三种遍历的区别如下图,图片来自邓老师书中.
Algorithm7 —— 二叉树的各种遍历_第1张图片

3.2.1 递归法

递归的方法非常简单, 代码区别只是visit的位置不一样.
前序遍历顺序如下图,图片来自邓老师书中.
Algorithm7 —— 二叉树的各种遍历_第2张图片

// 深度优先搜索算法(Depth First Search),是搜索算法的一种
    template <typename VST>
    void traversalPreRecur(BinNode<T>*node, VST& visit)
    {
        if (node)
        {
            visit(node->m_data);
            if (node->hasLchild())
            {
                traversalPreRecur(node->m_lchild, visit);
            }
            if (node->hasRchild())
            {
                traversalPreRecur(node->m_rchild, visit);
            }
        }
    }

中序遍历顺序如下图,图片来自邓老师书中.
Algorithm7 —— 二叉树的各种遍历_第3张图片

    template <typename VST>
    void traversalInRecur(BinNode<T>*node, VST& visit)
    {
        if (node)
        {
            if (node->hasLchild())
            {
                traversalInRecur(node->m_lchild, visit);
            }
            visit(node->m_data);
            if (node->hasRchild())
            {
                traversalInRecur(node->m_rchild, visit);
            }
        }
    }

后序遍历顺序如下图,图片来自邓老师书中.
Algorithm7 —— 二叉树的各种遍历_第4张图片

    template VST>
    void traversalPostRecur(BinNode<T>*node, VST& visit)
    {
        if (node)
        {
            if (node->hasLchild())
            {
                traversalPostRecur(node->m_lchild, visit);
            }
            if (node->hasRchild())
            {
                traversalPostRecur(node->m_rchild, visit);
            }
            // std::cout << node->m_data << " ";
            visit(node->m_data);
        }
    }

3.2.2 迭代法

值的一提的是迭代法前中后序遍历是使用一个辅助栈stack来实现的. 尤其注意前序遍历的进栈顺序是先右后左, 因为要保证先访问左边再访问右边.
细节如下

if (tmpNode->hasRchild())
{
   s.push(tmpNode->m_rchild);
}
if (tmpNode->hasLchild())
{
   s.push(tmpNode->m_lchild);
}

迭代法前序遍历顺序如下图,图片来自邓老师书中
Algorithm7 —— 二叉树的各种遍历_第5张图片

template <typename VST>
    void traversalPreIter(BinNode*node, VST& visit)// use stack
    {
        std::stack*> s;
        if (node)
        {
            s.push(node);
        }

        while (!s.empty())
        {
            BinNode* tmpNode = s.top();
            s.pop();
            visit(tmpNode->m_data);

            if (tmpNode->hasRchild())
            {
                s.push(tmpNode->m_rchild);
            }
            if (tmpNode->hasLchild())
            {
                s.push(tmpNode->m_lchild);
            }
        }
    }

迭代法中序遍历顺序如下图,图片来自邓老师书中
Algorithm7 —— 二叉树的各种遍历_第6张图片

    template <typename VST>
    void traversalInIter(BinNode*node, VST& visit)// use stack
    {
        std::stack*> s;

        while (true)
        {
            if (node)
            {
                s.push(node);
                node = node->m_lchild;
            }
            else if (!s.empty())
            {
                node = s.top();
                s.pop();
                visit(node->m_data);
                node = node->m_rchild;
            }
            else
                return;
        }
    }
    template <typename VST>
    void traversalPostIter(BinNode*node, VST& visit)// use stack
    {
        std::stack*> s;
        BinNode* preNodeVisit = nullptr;

        while (node) //移动到左子树最下边
        {
            s.push(node);
            node = node->m_lchild;
        }
        while (!s.empty())
        {
            node = s.top();
            s.pop();

            //一个根节点被访问的前提是:无右子树或右子树已被访问过
            if (!node->hasRchild() || node->m_rchild == preNodeVisit)
            {
                visit(node->m_data);
                preNodeVisit = node;
            }
            else// 右子树一定不为空
            {
                s.push(node);//根节点再次入栈
                node = node->m_rchild;
                while (node)//移动到右节点左子树最下边
                {
                    s.push(node);
                    node = node->m_lchild;
                }
            }
        }
    }

3.2.3 Morris Traversal法

占坑

你可能感兴趣的:(Algorithm,二叉树遍历算法,递归法,迭代法,Morris遍历法)