目录
1、二叉树结构的层次遍历
二叉树的遍历
二叉树的遍历(Traversing Binay Tree)是指从根结点出发,按照某种次序
依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。
通用树结构的层次遍历算法可以用在二叉树结构
设计思路(游标)
提供一组遍历相关的函数,按层次访问二叉树中的数据元素。
层次遍历算法
-原料: class LinkQueue<T>;
-游标: LinkQueue<T>: :front();
-思想
● begin() → 将根结点压入队列中
● current() → 访问队头元素指向的数据元素
● next() → 队头元素弹出,将队头元素的孩子压入队列中(核心)
● end() → 判断队列是否为空
层次遍历算法示例
2、编程实验
由于通用树结构(GTree)和 二叉树(BTree)都需要实现一组遍历相关函数所以在抽象父类Tree中设置一组纯虚函数
Tree.h新增:
virtual bool begin() = 0;
virtual bool end() = 0;
virtual bool next() = 0;
virtual T current() = 0;
BTree.h新增:
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(); // node指向队头
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 in current position ...");
}
}
为了安全性所以我们在删除结点后执行清空队列操作
void clear()
{
free(root());
m_queue.clear();
this->m_root = NULL;
}
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;
}
main.cpp
#include <iostream>
#include "BTree.h"
using namespace std;
using namespace DTLib;
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;
for(bt.begin(); !bt.end(); bt.next())
{
cout << bt.current() << " ";
}
return 0;
}
2、二叉树的典型遍历方式 (先序、中序、后序)
典型的二叉树遍历方式
-先序遍历 ( 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 选择执行遍历算法(先序,中序,后序)
● 返回值为堆中的数组对象(生命期由智能指针管理)
● 数组元素的次序反映遍历的先后次序
enum BTTraversal
{
PreOrder, // 先序遍历
InOrder, // 中序遍历
PostOrder, // 后序遍历
LeverOrder // 层次遍历
};
典型遍历使用示例
SharedPointer< Array<int> > sp = NULL;
sp = bt.traversal(PreOrder); // 先序遍历
for(int i=0; i<(*sp).length(); i++)
{
cout << (*sp)[i] << " ";
}
层次遍历算法示例
编程实验
二叉树的典遍历方式
#ifndef BTREE_H
#define BTREE_H
#include "Tree.h"
#include "BTreeNode.h"
#include "Exception.h"
#include "LinkQueue.h"
#include "DynamicArray.h"
namespace DTLib
{
enum BTTraversal
{
PreOrder,
InOrder,
PostOrder,
LeverOrder
};
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);
}
}
void leverOrderTraversal(BTreeNode<T>* node, LinkQueue<BTreeNode<T>*>& queue)
{
if(node != NULL)
{
LinkQueue<BTreeNode<T>*> tmp;
tmp.add(node);
while(tmp.length() > 0)
{
BTreeNode<T>* n = tmp.front();
if(n->left != NULL)
{
tmp.add(n->left);
}
if(n->right != NULL)
{
tmp.add(n->right);
}
tmp.remove();
queue.add(n);
}
}
}
void traversal(BTTraversal order, LinkQueue<BTreeNode<T>*>& queue)
{
switch( order )
{
case PreOrder:
preOrderTraversal(root(), queue);
break;
case InOrder:
inOrderTraversal(root(), queue);
break;
case PostOrder:
postOrderTraversal(root(), queue);
break;
case LeverOrder:
leverOrderTraversal(root(), queue);
break;
default:
THROW_EXCEPTION(InvalidParameterException, "Parameter order is invalid ...");
}
}
public:
// ...
SharedPointer< Array<T> > traversal(BTTraversal order)
{
DynamicArray<T>* ret = NULL;
LinkQueue<BTreeNode<T>*> queue;
traversal(order, queue); // 遍历,遍历结果保存在queue
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;
}
// ...
};
}
#endif // BTREE_H
main.cpp
#include <iostream>
#include "BTree.h"
using namespace std;
using namespace DTLib;
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);
SharedPointer< Array<int> > sp = NULL;
sp = bt.traversal(PreOrder);
for(int i=0; i<(*sp).length(); i++)
{
cout << (*sp)[i] << " ";
}
cout << endl;
return 0;
}
小结
二叉树的典型遍历都是以递归方式执行的
BTree以不同的函数接口支持典型遍历
层次遍历与典型遍历互不冲突
遍历结果能够反映树结点访问的先后次序
3、二叉树的线索化实现
什么是线索化二叉树?
-将二叉树转换为双向链表的过程(非线性 → 线性)
-能够反映某种二叉树的遍历次序(结点的先后访问次序)
● 利用结点的 right指针指向遍历中的后继结点
● 利用结点的 left指针指向遍历中的前驱结点
如何对二叉树进行线索化?
思维过程:
二叉树的线索化
函数接口设计
-BTreeNode<T>* thread(BTTraversal order)
● 根据参数 order 选择线索化的次序(先序,中序,后序,层次)
● 返回值线索化之后指向链表首结点的指针
● 线索化执行结束之后对应的二叉树变为空树
线索化流程
队列中结点的连接算法[ connect(queue) ]
编程实验
二叉树的线索化 thread (order)
template < typename T >
class BTree : public Tree<T>
{
protected:
// ...
BTreeNode<T>* connect(LinkQueue<BTreeNode<T>*>& queue)
{
BTreeNode<T>* ret = NULL;
if(queue.length() > 0)
{
ret = queue.front();
BTreeNode<T>* slider = queue.front();
queue.remove();
slider->left = NULL;
while(queue.length() > 0)
{
slider->right = queue.front();
queue.front()->left = slider;
slider = queue.front();
queue.remove();
}
slider->right = NULL; // 最后一个结点next指针为空
}
return ret;
}
public:
// ...
BTreeNode<T>* thread(BTTraversal order)
{
BTreeNode<T>* ret = NULL;
LinkQueue< BTreeNode<T>* > queue;
traversal(order, queue); // 遍历二叉树,将结果保存在queue队列
ret = connect(queue); // 将队列里已遍历好的有次序的结点连成双向链表
this->m_root = NULL; // 置为空树
m_queue.clear();
return ret;
}
};
main.cpp
#include <iostream>
#include "BTree.h"
using namespace std;
using namespace DTLib;
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 << "Old BTree: " << endl;
for(bt.begin(); !bt.end(); bt.next())
{
cout << bt.current() << " ";
}
cout << endl << endl;
BTreeNode<int>* head = bt.thread(LeverOrder);
while( head->right )
{
head = head->right;
}
while( head )
{
cout << head->value << " ";
head = head->left;
}
return 0;
}
小结
线索化是将二叉树转换为双向链表的过程
线索化之后结点间的先后次序符合某种遍历次序
线索化操作将破坏原二叉树结点间的父子关系
线索化之后二叉树将不再管理结点的生命期