树到二叉树的转换:
通用树结构:双亲孩子表示法:
每个结点都有一个指向其双亲的指针,每个结点都有若干个指向其孩子的指针。
另一种树结构模型:孩子兄弟表示法:
每个结点都有一个指向其第一个孩子的指针,每个结点都有一个指向其第一个右兄弟的指针。
孩子兄弟表示法的特点:
1、能够表示任意的树形结构
2、每个结点包含一个数据成员和两个指针成员
3、孩子结点指针和兄弟结点指针构成了“树杈”
二叉树的定义:
二叉树是由n个结点组成的有限集合,该集合或者为空,或者是由一个根节点加上两棵分别称为左子树和右子树的,互不相交的二叉树组成。
特殊的二叉树:
满二叉树(Full Binary Tree):如果二叉树中所有分支结点的度数都为2,且叶子结点都在同一层次上,则称这类二叉树为满二叉树。
完全二叉树(Complete Binary Tree):如果一课具有n个结点的高度为k的二叉树,它的每一个结点都与高度为k的满二叉树中编号为1-n的结点--对应,则称这棵二叉树为完全二叉树。(从上到下从左到右编号)
完全二叉树的特性:
同样结点数的二叉树,完全二叉树的高度最小。
完全二叉树的叶节点仅出现在最下面两层:最底层的叶结点一定出现在左边,倒数第二层的叶结点一定出现在右边,完全二叉树中度为1的结点只有左孩子。
小结:通用树结构采用了双亲结点表示法进行描述,孩子兄弟表示法有能力描述任意类型的树结构,孩子兄弟表示法能够将通用树转化为二叉树,二叉树是最多只有两个孩子的树。
61、二叉树的存储结构设计
完成二叉树和二叉树结点的存储结构设计。
设计要点:BTree为二叉树结构,每个结点最多只有两个后继结点。GTreeNode只包含4个固定的共有成员(哪4个?)。
实现树结构的所有操作(增,删,查,等)。
GTree(二叉树结构)的实现架构:
62、查找
基于数据元素值的查找:
BTreeNode<T>*find(const T& value)const
基于结点的查找:
BTreeNode<T>*find(TreeNode<T>* node)const
基于数据元素值的查找
定义功能:find(node,value)
在node为根结点的树中查找value所在的结点。
find(node,value)=
return node; node->value==value
find(node->left,value);or find(node->reght,value); node->value!=value
virtual BTreeNode<T>* find(BTreeNode<T>* node,const T& value) const
{
BTreeNode<T>* ret=NULL;
if(node != NULL)
{
if(node->value==value)
{
ret=node;
}
else
{
if(ret==NULL)
{
ret=find(node->left,value);
}
if(ret == NULL)
{
ret=find(node->right,value);
}
}
}
return ret;
}
基于结点的查找:
定义功能:find(node,obj)
在node为根节点的树中查找是否存在obj结点
find(node,obj)=
return node; node==obj
find(node->left,obj);or find(node->right,obj); node!=obj
virtual BTreeNode<T>* find(BTreeNode<T>* node,BTreeNode<T>* obj) const
{
BTreeNode<T>* ret=NULL;
if(node == obj)
{
ret=node;
}
else
{
if(node!=NULL)
{
if(ret==NULL)
{
ret=find(node->left,obj);
}
if(ret==NULL)
{
ret=find(node->right,obj);
}
}
}
return ret;
}
63、插入操作
是否能够在任意结点处插入子节点?是否需要指定新数据元素(新结点)的插入位置。
二叉树结点的位置枚举类型:
enum BTNodePos
{ ANY , LEFT , RIGHT }
插入方式:插入新结点:
bool insert(TreeNdoe<T>* node)
bool insert(TreeNode<T>* node,BTNodePos pos)
插入数据元素:
bool insert(const T& valude,TreeNode<T>* parent)
bool insert(const T& value,TreeNode<t>* parent,BTnodePos pos)
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;
}
bool insert(TreeNode<T>* node)
{
return insert(node,ANY);
}
virtual bool insert(TreeNode<T>* node,BTNodePos pos)//首先要实现父类的虚函数,所以此处还是TreeNode,下边还得转换一下
{
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,"is 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");
}
else
{
node->value= value;
node->parent=parent;
ret=insert(node,pos);
if( !ret)
{
delete node;
}
}
return ret;
}
小结:二叉树的插入操作需要指明插入的位置,插入操作必须正确处理指向父节点的指针,插入数据元素时需要从堆空间中创建结点,当数据元素插入失败时需要释放结点空间。
64、删除和清除
删除的方式:基于数据元素值的删除
SharedPointer<Tree<T>>remove(const T& value)
基于结点的删除
SharedPointer<Tree<T> >remove(TreeNode<T>* node)
删除操作功能的定义:
void remove(BTreeNode<T>* node,BTree<T>*& ret)
将node为根结点的子树从原来的二叉树中删除,ret作为子树返回(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 true..");
}
else
{
if(root()==node)
{
this->m_root=NULL;
}
else
{
BTreeNode<T>* parent=dynamic_cast<BTreeNode<T>*>(node->parent);//parent是TreeNode?
if(parent->left==node)
{
parent->left=NULL;
}
else if(parent->right==node)
{
parent->right=NULL;
}
node->parent=NULL;
}
ret->m_root=node;
}
}
SharedPointer<Tree<T> > remove(const T& value) //删除结点,不让后续结点也全部删除,返回sharepointer指向的一颗树
{
BTree<T>* ret=NULL;
BTreeNode<T>* node=find(value);
if(node==NULL)
{
THROW_EXCEPTION(InvalidParameterException,"can not find the node via value..");
}
else
{
remove(node,ret);
}
return ret;
}
SharedPointer<Tree<T> > remove(TreeNode<T>* node)
{
BTree<T>* ret=NULL;
node=find(node);
if(node==NULL)
{
THROW_EXCEPTION(InvalidParameterException,"Paratermeter node is invalid...");
}
else
{
remove(dynamic_cast<BTreeNode<T>*>(node),ret);
}
return ret;
}
清除操作的定义:
void clear():
将二叉树中的所有结点清除(释放堆中的结点)。
清除功能的定义:
free(node):
清除node为根节点的二叉树,释放二叉树中的每一个结点
free(node)=
return; node==NULL
free(node->left); free(node->right); delete node; node!=NULL
virtual void free(BTreeNode<T>* node)//功能函数
{
if(node!=NULL)
{
free(node->left);
free(node->right);
if(node->flag())
{
delete node;
}
}
}
void clear()
{
free(root());
this->m_root=NULL;
}
小结:删除操作将目标结点所代表的子树移除,删除操作必须完善处理父结点和子结点的关系,清除操作作用于销毁树中的每个结点,销毁结点时判断是否释放对应的内存空间(工厂模式)。
65、属性操作
1 二叉树中结点的数目
定义功能:count(node)
在node为根结点的二叉树中统计结点数目。
count(node)=
return 0; node==NULL
count(node->left)+count(node->right)+1; node!=NULL
int count(BTreeNode<T>* node) const//功能函数
{
/*int ret=0;
if(node!=NULL)
{
ret=count(node->left)+count(node->right)+1;
}*/
return (node!=NULL)? (count(node->left)+count(node->right)+1):0;
}
int count() const
{
return count(root());
}
2 二叉树的高度
定义功能:height(node)
获取node为根结点的二叉树的高度
height(node)=
return 0; node==NULL
MAX{height(node->left),height(node->right) }+1; node!=NULL
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 height() const
{
return height(root());
}
3 树的度数
定义功能:degree(node)
获取node为根结点的二叉树的度数
degree(node)=
return 0; node==NULL
MAX{degree(node->left),degree(node->right), !!node->left+!!node->right}; node!=NULL
!! 编程技巧,不为空为1,为空0.如果都不为空,值为2
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;
}
}
/*if(ret<2)
{
int d=degree(node->left);
if(ret<d)
{
ret=d;
}
if(ret<2)
{
int d=degree(node->right);
if(ret<d)
{
ret=d;
}
}
}*/
/*
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;
}
66、二叉树结构的层次遍历
二叉树的遍历:
二叉树的遍历(Traversing Binay Tree)是指从根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。
需要考虑的问题:通用树结构的层次遍历算法是否可以用在二叉树结构上?
如果可以,代码需要做怎样的改动?
设计思路(游标):与通用树一样
层次遍历算法:
原料:class LinkQueue<T>;
游标:LinkQueue<T>::front();
思想:begin() current() next() end()
next压入的不是链表而是左右孩子。
bool begin()
{
bool ret(root() != NULL);
if(ret)
{
m_queue.clear();//万一上一次遍历没结束,先clear,保证begin调用之后,队列中只有根节点
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 ...");
}}
for(bt.begin();!bt.end();bt.next())//测试
{
cout<<bt.current()<<" ";
}
cout<<endl;
67、二叉树的典型遍历方式
典型的二叉树遍历方式:
先序遍历(Pre-order Trabersal)<最先访问根结点
中序遍历(In--order Trabersal) 中间访问根结点
后序遍历(Post-order Trabersal) 最后访问根结点
先序遍历:二叉树为空:无操作,直接返回
二叉树不为空:1、访问根结点中的数据元素。2、先序遍历左子树。3、先序遍历右子树。
1-2-3-4-5-6-7-8-9-10->1-2-4-8-9-5-10-3-6-7
中序遍历:二叉树为空:无操作,直接返回
二叉树不为空:1、中序遍历左子树。2.访问根结点中的数据元素。3.中序遍历右子树。
1-2-3-4-5-6-7-8-9-10-> 8-4-9-2-10-5-1-6-3-7
后序遍历:二叉树为空:无操作,直接返回
二叉树不为空:1.后序遍历左子树。2、后序遍历右子树。3、访问根结点中的数据元素。
1-2-3-4-5-6-7-8-9-10->8-9-4-10-5-2-6-7-3-1
考虑的问题:是否可以将二叉树的典型遍历算法集成到BTree中?如果可以,代码怎么改?
设计要点:
不能与层次遍历函数冲突,必须设计新的函数接口。
算法执行完成后,能够方便的获得遍历结果。
遍历结果能够反映结点访问的先后顺序。
函数接口设计:
SharedPointer<Array<T> >traversal(BTTraversal order)
根据参数order选择执行遍历算法(先序,中序,后序),返回值为堆中的数据对象(生命期由智能指针管理),数组元素的次序反映遍历的先后次序。
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);
}
}
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以不同的函数接口支持典型遍历,层次遍历与典型遍历互不冲突,遍历结果能够反映树结点访问的先后次序。
68、二叉树的比较与相加
克隆:定义功能:clone(node)
拷贝node为根结点的二叉树(数据元素在对应位置相等)
clone=
return NULL; node=NULL
n=NewNode(); n->value=node->value; n->left=clone(node->left); n->right=clone(node->right); node!=NULL
BTreeNode<T>* clone(BTreeNode<T>* node)const
{
BTreeNode<T>* ret=NULL;
if(node!=NULL)
{
ret=BTreeNode<T>::NewNode();
if(ret!=NULL)
{
ret->value=node->value;
ret->left=clone(node->left);
ret->right=clone(node->right);
if(ret->left!=NULL)
{
ret->left->parent=ret;
}
if(ret->right!=NULL)
{
ret->right->parent=ret;
}
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"no memory");
}
}
return ret;
}
SharedPointer<BTree<T> > clone() const
{
BTree<T>* ret=new BTree<T>();
if(ret!=NULL)
{
ret->m_root = clone(root()); //BTreeNode赋值给TreeNode 子类对象能当父类对象使用
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memeory to create new tree...");
}
return ret;
}
2 二叉树比较操作的定义
判断两棵二叉树中的数据元素是否对应相等:
bool operator==(const BTree<T>& btree)
bool operator !=(const BTree<T>& BTree)
定义功能:equal(lh,rh)
判断lh为根结点的二叉树与rh为根结点的二叉树是否相等。
equal(lh,rh)=
return true; lh==rh
return false; lh==0&& rh!=0
return false; lh!=0 && rh==0
lh->value==rh->value&&equal(lh->left,rh->left)&&equal(lh-right,rh->right); lh!=0&& rh!=0
bool equal(BTreeNode<T>* lh,BTreeNode<T>* rh) const //功能函数
{
if(lh==rh)
{
return true;
}
else if((lh!=NULL)&&(rh!=NULL))
{
return (lh->value==rh->value)&&(equal(lh->left,rh->left))&&(equal(lh->right,rh->right));
}
else
{
return false;
}
}
bool operator ==(const BTree<T>& btree)
{
return equal(root(),btree.root());
}
bool operator !=(const BTree<T>& btree)
{
return !(*this==btree);
}
3 相加操作
SharedPointer<BTree<T> >add(const BTree<T>& btree)const
将当前二叉树与参数btree中的数据元素在对应位置处相加。
返回值(相加的结果)为堆空间中的一颗新的二叉树。
定义功能:add(lh,rh)
将lh为为根结点的二叉树与rh为根结点的二叉树相加
add(lh,rh)=
clone(rh); lh==0&& rh!=0
clone(lh); lh!=0&& rh==0
n=NewNode();n->value=lh->value+rh->value;n->left=add(lh->left,rh->left);n->right=add(lh->right,rh->right); lh!=0 && rh!=0
BTreeNode<T>* add(BTreeNode<T>* lh,BTreeNode<T>* rh) const
{
BTreeNode<T>* ret=NULL;
if((lh == NULL)&&( rh != NULL))
{
ret=clone(rh);
}
else if((lh!=NULL)&&(rh==NULL))
{
ret=clone(lh);
}
else if((lh!=NULL)&&(rh!=NULL))
{
ret=BTreeNode<T>::NewNode();
if(ret!=NULL)
{
ret->value=lh->value+rh->value;
ret->left=add(lh->left,rh->left);
ret->right=add(lh->right,rh->right);
if(ret->left !=NULL)
{
ret->left->parent=ret;
}
if(ret->right!=NULL)
{
ret->right->parent=ret;
}
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"NO MEMORY");
}
}
return ret;
}
SharedPointer<BTree<T> > add(const BTree<T>& btree)const
{
BTree<T>* ret=new BTree<T>();
if(ret!=NULL)
{
ret->m_root=add(root(),btree.root());
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"no memory");
}
return ret;
}
小结:比较操作判断两颗二叉树中的数据元素是否对应相等,克隆操作将当前二叉树在堆空间中进行复制,相加操作将两颗二叉树中的数据元素在对应位置处相加,相加操作的结果保存在堆空间的一颗二叉树中。
69、二叉树的线索化实现
线索化是将二叉树转换为双向链表的过程(非线性->线性)
能够反映某种二叉树的遍历次序(节点的先后访问次序):
利用结点的right指针指向遍历中的后继结点,利用结点的left指针指向遍历中的前驱结点。
思维过程:
线索化能够反映遍历次序->线索化算法必然与遍历算法相关->如何在遍历时记录结点间的访问次序->使用队列(先进先出)->遍历结束后队列记录了访问次序->循环访问队列,连接队列中的结点。
课程目标:新增功能函数traversal(order,queue)
新增遍历方式BTTraversal::LevelOrder 层次遍历
新增共有函数 BTreeNode<T>*thread(BTraversal order ) 线索化
消除遍历和线索化的代码冗余(代码重构)
函数接口设计:
BTreeNode<T>* thread(BTTraversal order)
根据参数order选择线索化的次序(先序,中序,后序,层次),返回值线索化之后指向链表首结点的指针,线索化执行结束之后对应的二叉树为空树。
连接算法:
void levelOrderTraversal(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);
}
}
}
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;
}
return ret;
}
SharedPointer<Array<T> >traversal(BTTraversal order)
{
DynamicArray<T>* ret=NULL;
LinkQueue<BTreeNode<T>*>queue; //保存遍历结点次序
traversal(order,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;
}
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;
}
小结:线索化是将二叉树转换为双向链表的过程,结点间的先后次序符合某种遍历次序,线索化操作将破坏原二叉树结点间的父子关系,线索化之后二叉树将不再管理结点的生命期。