一般树有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); } }