数据结构开发(24):二叉树中属性操作、层次遍历与典型遍历
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
: :front(); - 思想:
- 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