1、树的定义
(1)有且只有一个特殊的结点称为树的根结点(root),根结点没有前驱结点。
(2)当n>1时,除了根结点之外的其余的结点又被分成m个互不相交的子集。每个子集本身又是一棵树,这些树称为根结点的子树。
2、相关的术语
(1)结点的度、树的度
结点的度:结点所拥有所有子树的个数;
树的度:树中各结点度的最大值。
(2)叶子结点、分支结点
叶子结点:度为0的结点称为叶子结点;
分支结点:度不为0的结点称为分支结点。
(3)孩子、兄弟、双亲
孩子:树中一个结点的子树的根结点称为这个结点的孩子;
双亲:上述说法相应的称为双亲;
兄弟:具有同一个双亲的孩子结点互称兄弟。
(4)祖先、子孙
一个结点的所有子树中的结点称之为该节点的子孙结点,结点的祖先是从根结点
到该结点所经分支上的所有结点。
(5)结点的层数、树的深度
树既是一种递归结构也是一个层次结构,树中的每一个结点都处在一定的层次上。
树的深度:树中所有最大结点的最大层数称为树的深度。
(6)堂兄弟
双亲在同一层的结点互为堂兄弟。
(7)有序树、无序树
如果一棵树中结点的各个子树从左到右是有次序的,即若交换了某个结点各
子树的相对位置则构成不同的树,称这棵树为有序树。
反之,则称为无序树。
(7)森林
m(m>0)棵不相交的树的集合称为森林。自然界中树和森林是不同的概念。
但是在数据结构中,任何一棵树,删去根节点就变成森林了。
3、树的抽象数据类型
ADT Tree { Data: 树是由一个根结点和若干棵子树构成的. 树中结点具有相同数据类型及层次结构. Operation: //操作结果:构造一棵空树 InitTree(&T) //初始条件:串S是以广义表形式表示的树 //操作结果:按照S构造一棵树 CreateTree(&T,S) //初始条件:树T存在,Visit是对结点操作的应用函数 //操作结果:按某种次序对T的每个结点调用函数Visit() //一次且至多一次,一旦visit()失败,则操作失败. OrderTree(T,Visit()) //初始条件:树T存在 //操作结果:在树T中查找元素e,若查找成功,返回true //否则返回false SearchTree(T,&e) //初始条件:树T存在 //操作结果:以某种形式输出树T PrintTree(T) //初始条件:树T存在 //操作结果:求树T的深度 TreeDepth(T) //初始条件:树T存在 //操作结果:销毁树T DestroyTree(&T) }
4、树的存储结构
树中节点之间的逻辑关系都是父子关系,即通过双亲和孩子来刻画结点之间的逻辑关系,因而树的存储结构要求不仅存储各结点的数据信息,还要唯一地反映树中各结点之间的逻辑关系--父子关系,其关键是如何表示结点的双亲和孩子。根据结点中指针域所指的对象可分为双亲表示方法,孩子表示法和双亲孩子表示法。
(1)双亲表示法
a>数组表示
#define MAX_TREE_SIZE 100 //树中结点的最大个数 typedef struct { TElemType data; //结点的值 int parent; //结点的双亲在数组中的下标 }pTreeNode; //结点类型 pTreeNode pTree[MAX_TREE_SIZE];
b>链表表示
因为树中除了根节点以外,每一个结点只有一个双亲,所以这种表示方法得到的结果是一个单链表#define MAX_TREE_SIZE 100 //树中结点的最大个数 typedef struct { TElemType data; //结点的值 pTreeNode *parent; //结点的双亲指针 }pTreeNode; //结点类型
(2)孩子表示法
a>数组表示
#define MAX_SON_SIZE 3 //树的度 typedef struct { TElemType data; //结点的值 int Child[MAX_SON_SIZE]; //结点的孩子在数组中的下标 }CTreeNode; //数组元素类型 CTreeNode CTree[MAX_TREE_SIZE];
b>链表表示
typedef struct CTNode { TElemType data; //结点的值 struct CTNode *child[MAX_SON_SIZE]; //结点的指针域,指向孩子结点 }CTNode,*CTree; //结点的类型
c>左孩子右兄弟表示法
typedef struct LRTNode { TElemType data; //结点的值 struct LRTNode *lchild; //左孩子指针 struct LRTNode *rsibling; //右兄弟指针 }LRTNode,*LRTree; //结点的类型
(3)双亲孩子表示法
a>数组表示
typedef struct { TElemType data; //结点的值 int parent; //结点的双亲在数组中的下标 int child[MAX_SON_SIZE]; //结点的孩子在数组中的下标 }PCTreeNode; //数组元素的类型
b>链表表示法
typedef struct pCTNode { TElemType data; //结点的值 struct pCTNode *parent; //结点的双亲指针 struct pCTNode *child[MAX_SON_SIZE];//结点的孩子指针 }PCTNode,*pCTree; //结点类型
c>双亲数组孩子链表表示法
typedef struct Node { int child; //孩子结点在数组中的下标 struct Node *next; //孩子链表中的结点类型 }CNode; typedef struct { TElemType data; //结点的值 int parent; //结点的双亲在数组中的下标 CNode *Childlink; //指向孩子链表 }PCLink; //数组元素的类型
5、树的基本操作实现
孩子链表表示法:
typedef struct CTNode { TElemType data; //结点的值 struct CTNode *child[MAX_SON_SIZE]; //结点的指针域,指向孩子结点 }CTNode,*CTree; //结点的类型
(1)初始化操作
void InitTree(CTree &T) { T = NULL; }
(2)创建树操作
创建一棵树就是要在内存中存储一个树的结构。用孩子链表表示方法中就是在内存中生成一个孩子链表。因为树是一种层次结构,创建树的操作首先需要确定输入树的方法,然后再写出相应的算法。不同的输入方法,树的创建方法也不相同。如果采用广义表的形式进行输入:
使用广义表表示一棵树:A(B(D,E(H,I),F),D(G))
实现代码:
void CreateTree(CTree &T,char *S) { CTree stack[MS],p; int i=0,d[MS]; int top = -1; while (S[i]) { switch(S[i]) { case ' ': break; case '('://ѹջ top++; stack[top] = p; d[top] = 0; break; case ')'://կջ top--; break; case ',': d[top]++; break; default: if (!(p=(CTree)malloc(sizeof(CTNode)))) exit(1); p->data = S[i]; for (int i = 0;i < MAX_SON_SIZE;i++) p->child[i] = NULL; if (!T) T = p; else stack[top]->child[d[top]] = p; break; } i++; } }
(3)树的遍历
a 前序遍历
先访问根节点,然后从左到右依次先序遍历每一个子树,此遍历过程是一个递归的过程。对上图进行前序遍历得到的结果是:
A B D E H I F C G
<span style="font-weight: normal;">//前序遍历
void PreOrderTree(CTree T)
{
if (T)
{
cout << T->data << ' ';
for (int i=0;i<MAX_SON_SIZE;i++)
{
PreOrderTree(T->child[i]);
}
}
}</span>
b 后序遍历
先从左到右依次后序遍历根节点的一个子树,然后再访问根节点,这种遍历也是一个一个递归的过程,同样对上图进行遍历得到的结果是:
D H I E F B G C A
//后序遍历 void PostOrderTree(CTree T) { if (T) { for (int i=0; i<MAX_SON_SIZE; i++) { PostOrderTree(T->child[i]); } cout << T->data << ' '; } }
c 层序遍历
从树的第一层(根节点)开始,从上到下逐层遍历,在同一层中,则按照从左到右的顺序进行访问,直到整个树种的所有节点都被访问为止。对上图进行层序遍历的结果是:
A B C D E F G H I
void LevelOrderTree(CTree T) { SqQueue Q; //定义一个队列 CTree p; // InitQueue_Sq(Q,MAX_TREE_SIZE,10); //初始化队列 if(T) //非空跟指针进队 EnQueue_Sq(Q,T); while(!QueueEmpty_Sq(Q)) //非空队列循环 { DeQueue_Sq(Q,p); //队列首元素出队 cout<< p->data << ' '; //打印输出元素值 for(int i=0;i<MAX_SON_SIZE;++i) { if(p->child[i]) EnQueue_Sq(Q,p->child[i]); } } }