一般树有4种常用表示方法:
1.广义表表示法
2.父指针表示法
寻找父指针的操作时间复杂度为O(1),但寻找子女的操作时间复杂度达到O(n)。
3.子女链表表示法
适合需要频繁寻找子女的应用。
寻找子女的操作在子女链表中进行,时间复杂度为O(d),d为树的度。但寻找父结点的操作时间复杂度达到O(n)。
4.子女-兄弟链表示法
用二叉树表示一般树,每个结点的度都为2,由三个域组成:data、firstChild、nextSibling。
若要访问x结点的第k个子女,只要从x->firstChild开始沿着nextSibling继续走k-1步。当nextsibling指针为空时为最后一个子女。
适合需要频繁寻找子女的应用。
寻找子女的操作在子女链表中进行,时间复杂度为O(d),d为树的度。但寻找父结点的操作时间复杂度达到O(n)。
template <class T>
struct TreeNode{
T data;
TreeNode<T> *firstChild,*nextSibling;
TreeNode(T value=0,TreeNode<T> *fc=NULL,TreeNode<T> *ns=NULL):data(value),firstChild(fc),nextSibling(ns){};
};
template <class T>
class Tree{
private:
TreeNode<T> *root,*current; //如果不设*current指针,每次插入新结点都必须从头开始逐个结点比较
bool Find(TreeNode<T> *p,T value);
void RemovesubTree(TreeNode<T> *p);
bool FindParent(TreeNode<T> *t,TreeNode<T> *p);
public:
Tree(){root=current=NULL;};
bool Root();
bool Isempty(){return root==NULL;}
bool FirstChild();
bool NextSibling();
bool Parent();
bool Find(T target);
};
template <class T>
bool Tree<T>::Root(){
//寻找根使之成为当前结点
if(root==NULL){
current=NULL;
return false;
}
else{
current=root;
return true;
}
}
template <class T>
bool Tree<T>::Parent() {
//在树中寻找当前结点current的父结点,使之成为当前结点
TreeNode<T> *p=current;
if(current==NULL||current==root){
current=NULL;
return false;
}
return FindParent(root,p);
}
template <class T>
bool Tree<T>::FindParent(TreeNode<T> *t, TreeNode<T> *p) {
//在根为*t的树中寻找*p的父结点,并使之成为当前结点current
TreeNode<T> *q=t->firstChild;
bool succ;
while(q!=NULL && q!=p){ //没找到时,递归搜索子树
if((succ=FindParent(q,p))==true) return succ; //子树中找到
q=q->nextSibling; //子树中没找到,搜寻下一个兄弟
}
if(q!=NULL && q==p){
current=t;
return true;
}
else{
current=NULL;
return false;
}
}
template <class T>
bool Tree<T>::FirstChild() {
//在树中找当前结点的长子,并使之成为当前结点
if(current==NULL||current->firstChild==NULL){
current=NULL;
return false;
}
current=current->firstChild;
return true;
}
template <class T>
bool Tree<T>::NextSibling() {
//在树中找当前结点的下一个,并使之成为当前结点
if(current!=NULL&¤t->nextSibling!=NULL){
current=current->nextSibling;
return true;
}
current==NULL;
return false;
}
template <class T>
bool Tree<T>::Find(T target) {
if(Isempty()) return false;
return Find(root,target);
}
template <class T>
bool Tree<T>::Find(TreeNode<T> *p, T value) {
//在根为*p的树中找值为value的结点,找到后该结点成为当前结点,否则当前结点不变
bool result=false;
if(p->data==value){
result=true;
current=p;
}
else{
TreeNode<T> *q=p->firstChild;
while(q!=NULL && !(result=Find(q,value)))
q=q->nextSibling;
}
return result;
}
树的深度优先遍历通常包括先根次序遍历和后根次序遍历,不适合定义中序遍历,访问根结点操作的位置较难确定(子女个数不确定),因为子树没有顺序,只能人为定义
#include <iostream.h>
#include "tree.h"
#include "queue.h"
template <class T>
void PreOrder(ostream& out,TreeNode<T> *p){
//先根次序遍历并输出以*p为根的树:先访问树的根结点,再依次先根次序遍历树的每一棵子树
if(p!=NULL){
out<<p->data;
for(p=p->firstChild;p!=NULL;p=p->nextSibling) PreOrder(out,p);
}
}
template <class T>
void PostOrder(ostream& out,TreeNode<T> *p){
//后根次序遍历并输出以*p为根的树
if(p!=NULL){
TreeNode<T> *q;
for(q=p->firstChild;q!=NULL;q=q->nextSibling)
PostOrder(out,q);
out<<p->data;
}
}
template <class T>
void LevelOrder(ostream& out,TreeNode<T> *p){
//按广度优先次序(层次次序)分层遍历树,树的根结点是*p,算法中用到一个队列
Queue<TreeNode<T> *> Q;
if(p!=NULL){
Q.EnQueue(p);
while(!Q.IsEmpty()){
Q.DeQueue(p);
out<<p->data;
for(p=p->firstChild;p!=NULL;p=p->nextSibling)
Q=EnQueue(p);
}
}
}
可以用这种表示法下的二叉树表示森林,此时第一棵树的根的子树森林转化成左子树,剩余其它树组成的森林转换成右子树:
(1)先根次序依次遍历森林里每一棵树:前序遍历二叉树即可
template <class T>
void preorder(TreeNode<T> *t,void (*visit)(TreeNode<T> *p)){
if(t==NULL) return;
visit(t);
preorder(t->firstChild,visit);
preorder(t->nextSibling,visit);
}
(2)后根次序依次遍历森林里每一棵树:中序遍历二叉树即可
template <class T>
void postorder(TreeNode<T> *t,void (*visit)(TreeNode<T> *p)){
if(t==NULL) return;
postorder(t->firstChild,visit);
visit(t);
postorder(t->nextSibling,visit);
}
(3)层次序遍历整个森林:先把所有树的根结点加入队列
template <class T>
void Levelorder(TreeNode<T> *t,void (*visit)(TreeNode<T> *p)){
if(t==NULL) return;
Queue<TreeNode<T>*> Q;
for(;t!=NULL;t=t->nextSibling) Q.EnQueue(t);
while(!Q.IsEmpty()){
Q.DeQueue(t);
visit(t);
for(t=t->firstChild;t!=NULL;t=t->nextSibling) Q=EnQueue(t);
}
}