数据结构开发(24):二叉树中属性操作层次遍历与典型遍历

Posted pylearn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构开发(24):二叉树中属性操作层次遍历与典型遍历相关的知识,希望对你有一定的参考价值。

0.目录

1.二叉树中属性操作的实现

2.二叉树结构的层次遍历

3.二叉树的典型遍历方式

4.小结

1.二叉树中属性操作的实现

二叉树的属性操作:
技术分享图片

二叉树中结点的数目:

  • 定义功能:count(node)
    1. node 为根结点的二叉树中统计结点数目

技术分享图片

在BTree.h中实现统计结点数目:

protected:
    int count(BTreeNode<T>* node) const
    {
        int ret = 0;

        if( node != NULL )
        {
            ret = count(node->left) + count(node->right) + 1;
        }

        return ret;
    }
public:
    int count() const
    {
        return count(root());
    }

优化代码:

protected:
    int count(BTreeNode<T>* node) const
    {
        return (node != NULL) ? (count(node->left) + count(node->right) + 1) : 0;
    }
public:
    int count() const
    {
        return count(root());
    }

二叉树的高度:

  • 定义功能:height(node)
    1. 获取 node 为根结点的二叉树的高度

技术分享图片

在BTree.h中实现获取二叉树的高度:

protected:
    int height(BTreeNode<T>* node) const
    {
        int ret = 0;

        if( node != NULL )
        {
            int lh = height(node->left);
            int rh = height(node->right);

            ret = ((lh > rh) ? lh : rh) + 1;
        }

        return ret;
    }
public:
    int height() const
    {
        return height(root());
    }

树的度数:

  • 定义功能:degree(node)
    1. 获取 node 为根结点的二叉树的度数

技术分享图片

在BTree.h中实现获取二叉树的度数:

protected:
    int degree(BTreeNode<T>* node) const
    {
        int ret = 0;

        if( node != NULL )
        {
            int dl = degree(node->left);
            int dr = degree(node->right);

            ret = (!!node->left + !!node->right);

            if( ret < dl )
            {
                ret = dl;
            }

            if( ret < dr )
            {
                ret = dr;
            }
        }

        return ret;
    }
public:
    int degree() const
    {
        return degree(root());
    }

优化代码:

protected:
    int degree(BTreeNode<T>* node) const
    {
        int ret = 0;

        if( node != NULL )
        {
            BTreeNode<T>* child[] = { node->left, node->right };

            ret = (!!node->left + !!node->right);

            for(int i=0; (i<2) && (ret<2); i++)
            {
                int d = degree(child[i]);

                if( ret < d )
                {
                    ret = d;
                }
            }
        }

        return ret;
    }
public:
    int degree() const
    {
        return degree(root());
    }

统一mian.cpp测试:

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

using namespace std;
using namespace StLib;

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;

    return 0;
}

运行结果为:

10
4
2

2.二叉树结构的层次遍历

二叉树的遍历:

  • 二叉树的遍历 ( Traversing Binay Tree ) 是指从根结点出发按照某种次序依次访问二叉树中的所有结点使得每个结点被访问一次,且仅被访问一次。

需要考虑的问题:

  • 通用树结构的层次遍历算法是否可以用在二叉树结构上?
  • 如果可以代码需要做怎样的改动

设计思路 ( 游标 ):

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

技术分享图片

层次遍历算法:

  • 原料:class LinkQueue
  • 游标:LinkQueue
  • 思想:
    1. begin() → 将根结点压入队列中
    2. current() → 访问队头元素指向的数据元素
    3. next() → 队头元素弹出,将队头元素的孩子压入队列中 ( 核心 )
    4. end() → 判断队列是否为空

层次遍历算法示例:
技术分享图片

在BTree.h中实现二叉树结构的层次遍历:
(并且在clear()函数和remove()函数中要加上清空队列的操作。另外,将遍历操作的四个函数在父类中声明为虚函数。)

public:
    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();

            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 at current position ...");
        }
    }

mian.cpp测试:

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

using namespace std;
using namespace StLib;

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);

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

    cout << endl;

    return 0;
}

运行结果为:

1 2 3 4 5 6 7 8 9 10 

3.二叉树的典型遍历方式

问题:

  • 二叉树是否只有一种遍历方式 ( 层次遍历 ) ?

典型的二叉树遍历方式:

  • 先序遍历 ( Pre-Order Traversal )
  • 中序遍历 ( In-Order Traversal )
  • 后序遍历 ( Post-Order Traversal )

先序遍历 ( Pre-Order Traversal ):

  • 二叉树为空:
    1. 无操作,直接返回
  • 二叉树不为空:
    1. 访问根结点中的数据元素
    2. 先序遍历左子树
    3. 先序遍历右子树

技术分享图片

先序遍历功能定义:
技术分享图片

中序遍历 ( In-Order Traversal ):

  • 二叉树为空:
    1. 无操作,直接返回
  • 二叉树不为空:
    1. 中序遍历左子树
    2. 访问根结点中的数据元素
    3. 中序遍历右子树

技术分享图片

中序遍历功能定义:
技术分享图片

后序遍历 ( Post-Order Traversal ):

  • 二叉树为空:
    1. 无操作,直接返回
  • 二叉树不为空:
    1. 后序遍历左子树
    2. 后序遍历右子树
    3. 访问根结点中的数据元素

技术分享图片

后序遍历功能定义:
技术分享图片

需要考虑的问题:

  • 是否可以将二叉树的典型遍历算法集成到 BTree 中?
  • 如果可以代码需要做怎样的改动?

设计要点:

  • 不能与层次遍历函数冲突,必须设计新的函数接口
  • 算法执行完成后,能够方便的获得遍历结果
  • 遍历结果能够反映结点访问的先后次序

函数接口设计

  • SharedPointer< Array<T> > traversal(BTTraversal order)
    1. 根据参数 order 选择执行遍历算法 ( 先序,中序,后序 )
    2. 返回值为堆中的数组对象 ( 生命期由智能指针管理 )
    3. 数组元素的次序反映遍历的先后次序

典型遍历示例:
技术分享图片

二叉树的典型遍历方式:

#include "LinkQueue.h"
#include "DynamicArray.h"
namespace StLib
{

enum BTTraversal
{
    PreOrder,
    InOrder,
    PostOrder
};

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);
        }
    }
public:
    SharedPointer< Array<T> > traversal(BTTraversal order)
    {
        DynamicArray<T>* ret = NULL;
        LinkQueue<BTreeNode<T>*> queue;

        switch (order) {
        case PreOrder:
            preOrderTraversal(root(), queue);
            break;
        case InOrder:
            inOrderTraversal(root(), queue);
            break;
        case PostOrder:
            postOrderTraversal(root(), queue);
            break;
        default:
            THROW_EXCEPTION(InvalidParameterException, "Parameter order is invalid ...");
            break;
        }

        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;
    }
};

}

main.cpp测试:

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

using namespace std;
using namespace StLib;

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;

    cout << "层次遍历:" << endl;
    for(bt.begin(); !bt.end(); bt.next())
    {
        cout << bt.current() << " ";
    }
    cout << endl;

    SharedPointer< Array<int> > sp = NULL;

    sp = bt.traversal(PreOrder);

    cout << "先序遍历:" << endl;
    for(int i=0; i<(*sp).length(); i++)
    {
        cout << (*sp)[i] << " ";
    }
    cout << endl;

    sp = bt.traversal(InOrder);

    cout << "中序遍历:" << endl;
    for(int i=0; i<(*sp).length(); i++)
    {
        cout << (*sp)[i] << " ";
    }
    cout << endl;

    sp = bt.traversal(PostOrder);

    cout << "后序遍历:" << endl;
    for(int i=0; i<(*sp).length(); i++)
    {
        cout << (*sp)[i] << " ";
    }
    cout << endl;

    return 0;
}

运行结果为:

10
4
2
层次遍历:
1 2 3 4 5 6 7 8 9 10 
先序遍历:
1 2 4 8 9 5 10 3 6 7 
中序遍历:
8 4 9 2 10 5 1 6 3 7 
后序遍历:
8 9 4 10 5 2 6 7 3 1 

4.小结

  • 二叉树的典型遍历都是以递归方式执行的
  • BTree 以不同的函数接口支持典型遍历
  • 层次遍历与典型遍历互不冲突
  • 遍历结果能够反映树结点访问的先后次序

最终的BTree.h代码:
BTree.h

#ifndef BTREE_H
#define BTREE_H

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

namespace StLib
{

enum BTTraversal
{
    PreOrder,
    InOrder,
    PostOrder
};

template <typename T>
class BTree : public Tree<T>
{
protected:
    LinkQueue<BTreeNode<T>*> m_queue;

    virtual BTreeNode<T>* find(BTreeNode<T>* node, const T& value) const
    {
        BTreeNode<T>* ret = NULL;

        if( node != NULL )
        {
            if( node->value == value )
            {
                return node;
            }
            else
            {
                if( ret == NULL )
                {
                    ret = find(node->left, value);
                }

                if( ret == NULL )
                {
                    ret = find(node->right, value);
                }
            }
        }

        return ret;
    }

    virtual BTreeNode<T>* find(BTreeNode<T>* node, BTreeNode<T>* obj) const
    {
        BTreeNode<T>* ret = NULL;

        if( node == obj )
        {
            return node;
        }
        else
        {
            if( node != NULL )
            {
                if( ret == NULL )
                {
                    ret = find(node->left, obj);
                }

                if( ret == NULL )
                {
                    ret = find(node->right, obj);
                }
            }
        }

        return ret;
    }

    virtual bool insert(BTreeNode<T>* n, BTreeNode<T>* np, BTNodePos pos)
    {
        bool ret = true;

        if( pos == ANY )
        {
            if( np->left == NULL )
            {
                np->left = n;
            }
            else if( np->right == NULL )
            {
                np->right = n;
            }
            else
            {
                ret = false;
            }
        }
        else if( pos == LEFT )
        {
            if( np->left == NULL )
            {
                np->left = n;
            }
            else
            {
                ret = false;
            }
        }
        else if( pos == RIGHT )
        {
            if( np->right == NULL )
            {
                np->right = n;
            }
            else
            {
                ret = false;
            }
        }
        else
        {
            ret = false;
        }

        return ret;
    }

    virtual void remove(BTreeNode<T>* node, BTree<T>*& ret)
    {
        ret = new BTree<T>();

        if( ret == NULL )
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree ...");
        }
        else
        {
            if( root() == node )
            {
                this->m_root = NULL;
            }
            else
            {
                BTreeNode<T>* parent = dynamic_cast<BTreeNode<T>*>(node->parent);

                if( parent->left == node )
                {
                    parent->left = NULL;
                }
                else if( parent->right == node )
                {
                    parent->right = NULL;
                }

                node->parent = NULL;
            }

            ret->m_root = node;
        }
    }

    virtual void free(BTreeNode<T>* node)
    {
        if( node != NULL )
        {
            free(node->left);
            free(node->right);

            if( node->flag() )
            {
                delete node;
            }
        }
    }

    int count(BTreeNode<T>* node) const
    {
        return (node != NULL) ? (count(node->left) + count(node->right) + 1) : 0;
    }

    int height(BTreeNode<T>* node) const
    {
        int ret = 0;

        if( node != NULL )
        {
            int lh = height(node->left);
            int rh = height(node->right);

            ret = ((lh > rh) ? lh : rh) + 1;
        }

        return ret;
    }

    int degree(BTreeNode<T>* node) const
    {
        int ret = 0;

        if( node != NULL )
        {
            BTreeNode<T>* child[] = { node->left, node->right };

            ret = (!!node->left + !!node->right);

            for(int i=0; (i<2) && (ret<2); i++)
            {
                int d = degree(child[i]);

                if( ret < d )
                {
                    ret = d;
                }
            }
        }

        return ret;
    }

    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);
        }
    }
public:
    bool insert(TreeNode<T>* node)
    {
        return insert(node, ANY);
    }

    virtual bool insert(TreeNode<T>* node, BTNodePos pos)
    {
        bool ret = true;

        if( node != NULL )
        {
            if( this->m_root == NULL )
            {
                node->parent = NULL;
                this->m_root = node;
            }
            else
            {
                BTreeNode<T>* np = find(node->parent);

                if( np != NULL )
                {
                    ret = insert(dynamic_cast<BTreeNode<T>*>(node), np, pos);
                }
                else
                {
                    THROW_EXCEPTION(InvalidParameterException, "Invalid parent tree node ...");
                }
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException, "Parameter node can not be NULL ...");
        }

        return ret;
    }

    bool insert(const T& value, TreeNode<T>* parent)
    {
        return insert(value, parent, ANY);
    }

    virtual bool insert(const T& value, TreeNode<T>* parent, BTNodePos pos)
    {
        bool ret = true;
        BTreeNode<T>* node = BTreeNode<T>::NewNode();

        if( node == NULL )
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new node ...");
        }
        else
        {
            node->value = value;
            node->parent = parent;

            ret = insert(node, pos);

            if( !ret )
            {
                delete node;
            }
        }

        return ret;
    }

    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;
    }

    BTreeNode<T>* find(const T& value) const
    {
        return find(root(), value);
    }

    BTreeNode<T>* find(TreeNode<T>* node) const
    {
        return find(root(), dynamic_cast<BTreeNode<T>*>(node));
    }

    BTreeNode<T>* root() const
    {
        return dynamic_cast<BTreeNode<T>*>(this->m_root);
    }

    int degree() const
    {
        return degree(root());
    }

    int count() const
    {
        return count(root());
    }

    int height() const
    {
        return height(root());
    }

    void clear()
    {
        free(root());

        m_queue.clear();

        this->m_root = NULL;
    }

    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();

            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 at current position ...");
        }
    }

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

        switch (order) {
        case PreOrder:
            preOrderTraversal(root(), queue);
            break;
        case InOrder:
            inOrderTraversal(root(), queue);
            break;
        case PostOrder:
            postOrderTraversal(root(), queue);
            break;
        default:
            THROW_EXCEPTION(InvalidParameterException, "Parameter order is invalid ...");
            break;
        }

        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;
    }

    ~BTree()
    {
        clear();
    }
};

}

#endif // BTREE_H








以上是关于数据结构开发(24):二叉树中属性操作层次遍历与典型遍历的主要内容,如果未能解决你的问题,请参考以下文章

Swift 数据结构 - 二叉树(Binary Tree)

二叉树层次遍历(包含C语言实现代码)

二叉树的遍历算法

4685: 二叉树中序遍历

LeetCode 107 ——二叉树的层次遍历 II

数据结构(C语言版)严蔚敏->二叉树(链式存储结构)的构造及其几种遍历方式(先序中序后序层次)和线索二叉树