1. 程式人生 > >資料結構開發(24):二叉樹中屬性操作、層次遍歷與典型遍歷

資料結構開發(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