資料結構開發(24):二叉樹中屬性操作、層次遍歷與典型遍歷
阿新 • • 發佈:2018-12-23
0.目錄
1.二叉樹中屬性操作的實現
2.二叉樹結構的層次遍歷
3.二叉樹的典型遍歷方式
4.小結
1.二叉樹中屬性操作的實現
二叉樹的屬性操作:
二叉樹中結點的數目:
- 定義功能:count(node)
- 在 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)
- 獲取 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)
- 獲取 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
- 思想:
- begin() → 將根結點壓入佇列中
- current() → 訪問隊頭元素指向的資料元素
- next() → 隊頭元素彈出,將隊頭元素的孩子壓入佇列中 ( 核心 )
- 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 ):
- 二叉樹為空:
- 無操作,直接返回
- 二叉樹不為空:
- 訪問根結點中的資料元素
- 先序遍歷左子樹
- 先序遍歷右子樹
先序遍歷功能定義:
中序遍歷 ( In-Order Traversal ):
- 二叉樹為空:
- 無操作,直接返回
- 二叉樹不為空:
- 中序遍歷左子樹
- 訪問根結點中的資料元素
- 中序遍歷右子樹
中序遍歷功能定義:
後序遍歷 ( Post-Order Traversal ):
- 二叉樹為空:
- 無操作,直接返回
- 二叉樹不為空:
- 後序遍歷左子樹
- 後序遍歷右子樹
- 訪問根結點中的資料元素
後序遍歷功能定義:
需要考慮的問題:
- 是否可以將二叉樹的典型遍歷演算法整合到 BTree 中?
- 如果可以,程式碼需要做怎樣的改動?
設計要點:
- 不能與層次遍歷函式衝突,必須設計新的函式介面
- 演算法執行完成後,能夠方便的獲得遍歷結果
- 遍歷結果能夠反映結點訪問的先後次序
函式介面設計
SharedPointer< Array<T> > traversal(BTTraversal order)
- 根據引數 order 選擇執行遍歷演算法 ( 先序,中序,後序 )
- 返回值為堆中的陣列物件 ( 生命期由智慧指標管理 )
- 陣列元素的次序反映遍歷的先後次序
典型遍歷示例:
二叉樹的典型遍歷方式:
#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