Keep up with the previous article.
链式存储结构
在链式存储结构中我们能更形象地,且更易于理解的描绘二叉树。由二叉树的定义可知,二叉树的结点由一个数据元素和分别指向其左子树和右子树的两个分支构成,则表示二叉树的链表中的结点至少包含3个域:数据域和左右指针域。有时呢,为了便于查找,还可以加一个指向其双亲的指针域。利用这两种结点结构所得二叉树的存储结构分别称之为二叉链表和三叉链表。
容易证得,在含有n个结点的二叉链表有n+1个空链域。由此我们可初始化一下二叉链表:
// Status是函数的类型,其值是函数结果状态代码
typedef int Status;
// TElemType是数据元素的类型
typedef char TElemType;
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode,*BiTree;
Status CreateBiTree(BiTree *T)
{
//按先序次序输入二叉树中结点的值,空格表空
char val;
scanf("%c",&val);
getchar(); //清个缓存
if(val==' ') *T=NULL;
else{
if(!(*T=(BiTNode *)malloc(sizeof(BiTNode)))) exit(OVERFLOW);
(*T)->data=val;
CreateBiTree(&(*T)->lchild);
CreateBiTree(&(*T)->rchild);
}
return OK;
}
这里采用的是先序遍历的方式插入各结点,该方式简单,易于理解。
遍历二叉树
遍历对线性结构来说是一个很简单的事,但对于二叉树则不然。因其是非线性结构,每个结点都可能有两颗子树,因而需要寻找一种规律,以便使二叉树上的结点能排列在一个线性队列上,从而便于遍历。
假如以L、D、R分别表示遍历左子树、访问根结点和遍历右子树,则可能有6种遍历二叉树的方案。若限定先左后右,则只有3种情况——DLR,LDR,LRD。再基于二叉树的递归定义,可得下述遍历二叉树的递归算法定义。
先序遍历-DLR
若二叉树为空,则空操作,否则:
- 访问根结点
- 遍历左子树
- 遍历右子树
具体算法如下:
Status PreOrderTraverse(BiTree T)
{
if(T){
if(VistBiTree(T->data))
if(PreOrderTraverse(T->lchild))
if(PreOrderTraverse(T->rchild)) return OK;
return ERROR;
}else return OK;
}
中序遍历-LDR
若二叉树为空,则空操作,否则:
- 遍历左子树
- 访问根结点
- 遍历右子树
具体算法如下:
Status InOrderTraverse(BiTree T)
{
if(T){
if(InOrderTraverse(T->lchild))
if(VistBiTree(T->data))
if(InOrderTraverse(T->rchild)) return OK;
return ERROR;
}else return OK;
}
后序遍历-LRD
若二叉树为空,则空操作,否则:
- 遍历左子树
- 遍历右子树
- 访问根结点
具体算法如下:
Status PostOrderTraverse(BiTree T)
{
if(T){
if(PostOrderTraverse(T->lchild))
if(PostOrderTraverse(T->rchild))
if(VistBiTree(T->data)) return OK;
return ERROR;
}else return OK;
}
以上的 VistBiTree函数,即访问结点的data值,需自己建立。