树(Tree)是n(n>=0)个节点的有限集。
在任意一颗非空树中:
(1)有且仅有一个特定的称为根(Root)的结点;
(2)当n>1时,其余节点可分为m(m>0)个互不相交的有限集,其中每一个集合本身又是一棵树,并称为根节点的子树(SubTree)
如图:
树的概念
结点(Degree):包含一个元素及若干指向其子树的结点。
结点的度:结点所拥有的子树数。
叶子(Leaf)或终端结点:度为0的结点。
非终端结点或分支结点:度数不为0的结点。
树的度:树内各结点的度的最大值。
孩子(Child):结点的子树的根称为该结点的孩子。
双亲(Parent):孩子的父结点。
兄弟(Sibling):同一双亲的孩子之间互称为兄弟。
堂兄弟:其双亲在同一层的结点互为堂兄弟。
祖先:从根到该结点所经分支的所有结点。
子孙:以某结点为根的子树中的任一结点都称为该结点的子孙。
结点的层次(Level):从根开始定义起,根为第一层。
树的深度(Depth)或高度:树中结点的最大层次称为树的深度。
森林(Forest):m(m>=0)棵互不相交的树的集合。
树的存储结构
双亲表示法
在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。
data | parent |
data是数据域,parent是指针域,存储该结点的双亲在数组中的下标。
/*树的双亲表示法结点结构定义*/
# define MAX_TREE_SIZE 100
type int ElemType;
typedef struct PTNode
{
TElemType data;/*结点数据*/
int parent;/*双亲位置*/
}PTNode;
typedef struct
{
PTNode nodes[MAX_TREE_SIZE];/*结点数组*/
int r,n;/*根的位置和结点数*/
}PTree;
指针域可以根据实际要求扩展结构,双亲域、长子域、左兄弟域。
孩子表示法
每个结点有多个指针域,其中每个指针指向子树的根结点,这种表示方法叫做多重链表表示法。
一、指针域的个数等于树的度
这种方法适用于树中个结点的度相差很小,每个结点的空间都别充分利用。
二、每个结点指针域的个数等于该结点的度,取一个位置存储结点域的个数
适用于各结点的度相差比较大时。
由于各结点的子树有不同结构,在加上要维护结点的度的数值,造成时间上的损耗。
改进
把每个结点的孩子排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。n个头指针组成一个线性表,采用顺序存储结构。
/*树的孩子表示法结构定义*/
/*
需要设计两种结点结构
一种是孩子链表的孩子结点 |child|next|【第一个域表示该结点所指向的结点,next表示该结点同一层级的相邻结点】
另一种是表头数组的表头结点 |data|firstchild|【第一个域表示结点的数据,第二个结点表示与该结点子树的 第一个孩子的下标】
*/
# define MAX_TREE_SIZE 100
/*孩子结点*/
typedef struct CTNode
{
int child;
struct CTNode *next;
}*ChildPtr;
/*表头结构*/
typedef struct
{
TElemType data;
ChildPtr firstchild;
};
/*树结构*/
{
CTBox[MAX_TREE_SIZE];/*结点数组*/
int r,n;/*根位置和结点树*/
}
双亲孩子表示法
多用一个位置表示结点的双亲。
孩子兄弟表示法
任意一棵树,如果他的第一个孩子存在,则唯一,它的右兄弟如果存在则唯一。
所以可设两个指针指向该结点的第一个孩子,和该结点的右兄弟。
data | firstchild | rightsib |
/*树的孩子兄弟表示法结构*/
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;
需要时也可以增加一个双亲指针域来快速查找双亲。
二叉树
二叉树(Binary Tree)的特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树有左右之分,其次序不能颠倒。
ADT
ADT BinaryTree{
数据对象D:D是具有相同特性的数据元素的集合
数据关系R:
若D = Φ,则R = Φ,称BinaryTree为空二叉树
若D ≠ Φ,则R = {H},H有如下二元关系:
(1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2)若D-{root} ≠ Φ,则存在D - {root} = {Dl,Dr},且Dl ∩ Dr = Φ;
(3)若Dl ≠ Φ,则Dl中存在唯一的元素xl,<root,xl>∈H,且存在Dl上的关系Hl⊂H;
若Dr ≠ Φ,则Dr中存在唯一的元素xr,<root,xr>∈H,且存在Dr上的关系Hr⊂H;
H = {<root,xl>,<root,xr>,Hl,Hr};
(4)(Dl,{Hl})是一棵符合本定义的二叉树,称为根的左子树,
(Dr,{Hr})是一棵符合本定义的二叉树,称为根的右子树;
基本操作P:
InitBiTree(&T);
操作结果:构造空的二叉树T。
DestoryBiTree(&T);
初始条件:二叉树T存在。
操作结构:销毁二叉树T。
CreateBiTree(&T,definition);
初始条件:definition给出二叉树T的定义。
操作结果:按definition构造二叉树T。
ClearBiTree(&T);
初始条件:二叉树T存在。
操作结果:将二叉树清为空树。
BiTreeEmpty(T);
初始条件:二叉树T存在。
操作结果:若T为空二叉树,返回TRUE,否则返回FALSE。
BiTreeDepth(T);
初始条件:二叉树T存在。
操作结果:返回T的深度。
Root(T);
初始条件:二叉树T的根存在。
操作结果:返回T的根。
Value(T,e);
初始条件:二叉树T存在,e是T中某个结点。
操作结果:返回e的值。
Assign(T,&e,value);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:结点e赋值为value。
Parent(T,e);
初始条件:二叉树T存在,e是T中的结点。
操作结果:若e是T的非根结点,则返回它的双亲,否则返回"空"。
LeftChild(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的左孩子。若e无左孩子,则返回"空"。
RightChild(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的右孩子。若e无右孩子,则返回"空"。
LeftSibling(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的左兄弟。若e是T的左孩子或无左兄弟,则返回"空"。
RightSibling(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的右兄弟。若e是T的右孩子或无右兄弟,则返回"空"。
InsertChild(T,p,LR,c);
初始条件:二叉树T存在,p指向T中的某个结点,LR为0或1,非空二叉树c与T不想交且右子树为空。
操作结果:根据LR为0或1,插入c为T中p所指结点的左或右子树。p所指结点的原有左或右子树则成为c的右子树。
DeleteChile(T,p,LR);
初始条件:二叉树T存在,p指向T中的某个结点,LR为0或1.
操作结果:根据LR为0或1,删除T中p所指结点的左或右子树。
PreOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:先序遍历T,对每个结点调用函数visit一次且仅一次。一旦visit失败,则操作失败。
InOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:中序遍历T,对每个结点调用函数visit一次且仅一次。一旦visit失败,则操作失败。
PostOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:后序遍历T,对每个结点调用函数visit一次且仅一次。一旦visit失败,则操作失败。
LevelOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:层序遍历T,对每个结点调用函数visit一次仅且一次。一旦visit失败,则操作失败。
}ADT BinaryTree
二叉树的五种基本形态
1、空树
2、只有一个根结点
3、根结点只有左子树
4、根结点只有右子树
5、根结点既有左子树又有右子树
特殊的二叉树
1、斜树
右斜树:所有结点都只有左子树的二叉树
左斜树:所有结点都只有右子树的二叉树
2、满二叉树
所有分支结点都存在左子树和有子树,并且叶子都在同一层上。
特点:
(1)叶子只能出现在最下一层,出现在其他层就不能达成平衡。
(2)非叶子结点的度一定是2。
(3)在同样深度的二叉树中,满二叉树的结点个数最多,叶子最多。
3、完全二叉树
二叉树具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同,则称该二叉树为完全二叉树。
特点
(1)叶子结点只出现在最下两层
(2)最下层叶子一定集中在左部连续的位置
(3)倒数第二层,若有叶子结点,一定都在右部连续位置
(4)如果结点的度为1,则该结点只有左孩子,不存在右子树情况
(5)同样结点数的二叉树,完全二叉树深度最小【不存在空挡】
二叉树的性质
性质一:第i层上最多有个结点(i>=1)
性质二:深度为k的二叉树至多有-1个结点(i>=1)
性质三:任何一棵二叉树T,其叶子结点为n0,度为2的结点数为n2,则n0=n2+1
任意一棵树,其叶子结点的个数n0=(k-1)+(k-2)+(k-3)+...+1
性质四:具有n个结点的完全二叉树深度为 ,【向下取整】
性质五:如果有一棵有n个结点的完全二叉树(深度为)的结点按层序编号,对任一结点i有(1 <= n <=n)
1.如果i=1,则结点i是二叉树的跟,无双亲;如果i>1,则其双亲是结点⌊i/2⌋。
2.如果2i > n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i。
3.如果2i+1 > n,则结点i右孩子,否则其右孩子是结点 2i+1。
二叉树的存储结构
二叉树的顺序存储结构
完全二叉树适用于这种存储结构,将结点的编号对应到数组的相应位置上。
二叉树的链式存储结构
二叉链表:一个数据域,两个指针域
可根据具体需求增加双亲指针域,称为三叉链表
/*二叉树的二叉链表结点结构*/
typedef struct BiNode
{
TElemeType data;
struct BiNode *lchild,*rchild;
}BiNode,*BTree;
遍历二叉树
Traverse Binary Tree:从根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。【限定先左后右】
先序(根)遍历
若二叉树为空,则空操作;否则:
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树;
Status PreOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
if(T)
{
if(visit(T->data))
if(PreOrderTraverse(T->lchild,visit))
if(PreOrderTraverse(T->rchild,visit))
return OK;
return ERROR;
}
else
return OK;
}
中序(根)遍历
若二叉树为空,则空操作;否则:
(1)中序遍历左子树;
(2)访问根结点;
(3)中序遍历右子树;
/*递归遍历二叉树*/
Status InOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
if(T)
{
if(InOrderTraverse(T->lchild,visit))
if(visit(T->data))
if(InOrderTraverse(T->rchild,visit))
return OK;
return ERROR;
}
else
return OK;
}
当栈顶记录中的指针非空时,应遍历左子树,即指向左子树的指针进栈;
若栈顶记录中的指针为空,则应退至上一层,若是从左子树返回,则应访问当前层即栈顶记录中指针所指的根结点。
若是从右子树返回,则表明当前层的遍历结束,应继续退栈。【不必保存根结点,只需修改栈顶记录即可】
/*中序遍历的非递归实现*/
Status InOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
InitStack(S);
Push(S,T);//根指针进栈
while(!StackElempty(S))
{
while(GetTop(S,p))
push(S,p->lchild);//向左走到头
Pop(S,p);//空指针退栈
if(!StackEmpty(S))//访问结点,向右走一步
{
Pop(S,p);
if(!visit(p->data))
{
return ERROR;
}
Push(S,p->rchild);
}
}
}
Status InOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
InitStack(S);
p = T;
while(p || !StackEmpty(S))
{
if(p)
{//根指针进栈,遍历左子树
Push(S,p);
p = p->lchild;
}
else
{//根指针退栈,访问根结点,遍历右子树
Pop(S,p);
if(!visit(p->data))
return ERROR;
p = p->rchild;
}
}
}
后序(根)遍历
若二叉树为空,则空操作;否则:
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)遍历根结点;
Status PostOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
if(T)
{
if(PostOrderTraverse(T->lchild,visit))
if(PostOrderTraverse(T->rchild,visit))
if(visit(T->data))
return OK;
return ERROR;
}
else
return OK;
}
推导遍历结果
前序和中序————后序
中序和后序————前序
前序和后序不能确定一棵二叉树,所以不能确定中序序列
二叉树的建立
扩展二叉树:
为了便于建立二叉树,需要对二叉树的每个结点的空指针引出一个虚结点,其值为以特定值。
/*按前序输入二叉树的结点的值(字符)*/
/* .表示空树,构建二叉链表表示二叉树*/
Status CreateBiTree(BiTree &T)
{
scanf(&ch);
if(ch == ".")
T = NULL;
else
{
if(!(T = (BiTNode*)malloc(sizeof(BiTnode))))
exit(OVERFLOW);
T->data = ch;//生成根结点
CreateBiTree(T->lchild);//构建左子树
createBiTree(T->rchild);//构建右子树
}
return OK;
}
二叉树常用操作代码实现
// 二叉树操作.cpp : 定义控制台应用程序的入口点。
//由于在vs中编写的代码,若scanf_s处出错可以改为scanf
#include "stdafx.h"
# include<stdio.h>
# include<algorithm>
# include<stdlib.h>
# include<stdexcept>
# include<queue>
# include<stack>
# include<iostream>
using namespace std;
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode *lChild;
struct BiTNode *rChild;
}BiTNode;//二叉树结点
typedef BiTNode *BiTree;//指向二叉树结点的指针
void InitBiTree(BiTree &T) {
T = NULL;
}//初始化二叉树
void ClearBiTree(BiTree &T) {
if (T) {
if (T->lChild)
ClearBiTree(T->lChild);
if (T->rChild)
ClearBiTree(T->rChild);
free(T);
T = NULL;
}
}//清空二叉树
bool BiTreeEmpty(BiTree T) {
return T == NULL ? true : false;
}//判断二叉树是否为空
bool CreateBiTree(BiTree &T) {
char ch;
scanf_s("%c",&ch);
if (ch == '.')
T = NULL;
else {
try {
if (!(T = (BiTNode *)malloc(sizeof(BiTNode))))
throw "内存分配不成功!";
}
catch(const char* msg){
printf("%s",msg);
return false;
}
T->data = ch;
CreateBiTree(T->lChild);
CreateBiTree(T->rChild);
}
return true;
}//先序创建二叉树
/*非递归创建二叉树*/
// 二叉树的建立(非递归)(按照先序遍历的顺序来生成一颗二叉树)
BiTree createBiTree()
{
char ch[20];
cin >> ch;
int len = (int)strlen(ch);
stack<BiTree> s;// 用来存储节点地址的栈
int flag = 1; // 标志位
int i = 0;
if (ch[i] == '.')
return NULL;
BiTree temp;
BiTree root = new BiTNode();
root->data = ch[i++]; // 此处应该加一个判断首字符是否为‘#’的判断,保证根节点不为空。
root->lChild = NULL;
root->rChild = NULL;
s.push(root);// 根节点入栈
while (i < len)
{
BiTree pNew = NULL;
if (flag == 1) // 创建左孩子
{
if (ch[i] == '.')
flag = 2;
else
{
pNew = new BiTNode();
pNew->data = ch[i];
pNew->lChild = NULL;
pNew->rChild = NULL;
temp = s.top(); // 栈顶元素(取出栈顶元素赋予temp,注意,这不是出栈操作,因为栈顶指针top没有变)
temp->lChild = pNew;
s.push(pNew); // 栈顶元素的左子树入栈
flag = 1;
}
}
else if (flag == 2) // 创建右孩子
{
if (ch[i] == '.')
flag = 3;
else
{
pNew = new BiTNode();
pNew->data = ch[i];
pNew->lChild = NULL;
pNew->rChild = NULL;
temp = s.top(); // 栈顶元素
temp->rChild = pNew;
s.push(pNew); // 栈顶元素的右子树入栈
flag = 1;
}
}
else // 左右孩子都已经创建完毕
{
temp = s.top();
s.pop(); // 栈顶元素出栈,并修改栈顶指针
while (!s.empty() && s.top()->rChild == temp) // 若此时栈中的元素个数仍大于1个,并且刚刚出栈的元素是当前栈顶元素的右子树,则继续让栈顶元素出栈,并修改栈顶指针。
s.pop();
flag = 2; // 跳出此while循环时,创建当前栈顶节点的右孩子,此时的i在ch[]中对应的数据刚好就是下一个待使用的数据,所以执行 --i是为了防止下面的 ++i跳过了该数据(即当前i对应的数据就是我们下一步创建右子树所应该使用的数据)。
--i;
}
++i;
}
return root;
}
int BiTreeDepth(BiTree T) {
int LD, RD;
if (T == NULL)
return 0;
else {
LD = BiTreeDepth(T->lChild);
RD = BiTreeDepth(T->rChild);
return max(LD, RD) + 1;
}
}//求树深度
TElemType Value(BiTree p) {
return p->data;
}//返回结点的值
void Assign(BiTree p, TElemType value) {
p->data = value;
}//为二叉树某结点赋值
BiTree Location(BiTree T, TElemType e) {
BiTree p = NULL;
if (T) {
if (T->data == e)
return T;
else
{
if (p = Location(T->lChild, e))
return p;
if (p = Location(T->rChild, e))
return p;
}
}
return p;
}
bool InsertBiTree(BiTree T, TElemType e, BiTree T0, int LR) {
BiTree p = Location(T, e);
if (p) {
T0->rChild = LR ? p->lChild : p->rChild;
LR ? p->lChild = T0 : p->rChild = T0;
return true;
}
return false;
}//将T0插入树T,成为结点e的子树,LR为左右子树标志,T0只有左子树
bool DeleteBiTree(BiTree T, TElemType e, int LR) {
BiTree p = Location(T, e);
if (p) {
LR ? ClearBiTree(p->lChild) : ClearBiTree(p->rChild);
return true;
}
return false;
}//删除e结点的左子树右子树,LR为删除标志
void Visit(TElemType e) {
printf("%c",e);
}
void PreOrderTraverse(BiTree T, void(Visit)(TElemType)) {
if (T) {
Visit(T->data);
PreOrderTraverse(T->lChild, Visit);
PreOrderTraverse(T->rChild, Visit);
}
}//先序遍历
void InOrderTraverse(BiTree T, void(Visit)(TElemType)) {
if (T) {
PreOrderTraverse(T->lChild, Visit);
Visit(T->data);
PreOrderTraverse(T->rChild, Visit);
}
}//中序遍历
void PostOrderTraverse(BiTree T, void(Visit)(TElemType)) {
if (T) {
PreOrderTraverse(T->lChild, Visit);
PreOrderTraverse(T->rChild, Visit);
Visit(T->data);
}
}//后序遍历
/*非递归遍历树*/
void F_PostOrderTraverse(BiTree &T) {
stack<BiTree> s;
BiTree cur, pre = NULL;
s.push(T);
while (!s.empty()) {
cur = s.top();
if ((cur->lChild == NULL && cur->rChild == NULL) || (pre !=NULL && (pre == cur->lChild || pre == cur->rChild))) {//如果当前节点没有孩子结点或左右结点均已访问
cout << cur->data;
s.pop();
pre = cur;
}
else
{
if (cur->rChild != NULL)
s.push(cur->rChild);
if (cur->lChild != NULL)
s.push(cur->lChild);
}
}
}
void LevelOrderTraverse(BiTree T, void(Visit)(TElemType)) {
BiTree p[100];//数指针数组
int i, j;
i = j = 0;
if (T)
p[j++] = T;
while (i < j) {
Visit(p[i]->data);
if (p[i]->lChild)
LevelOrderTraverse(p[i]->lChild, Visit);
if (p[i]->rChild)
LevelOrderTraverse(p[i]->rChild, Visit);
i++;
}
}//层序遍历
void PrintBiTree(BiTree &T, void(Visit)(TElemType)) {
int row, col;
int i, j, m, l, r;
BiTNode a[101][101] = {};
if (T)
{
row = BiTreeDepth(T);
col = pow(2, row) - 1;
for (i = 1; i <= row - 1; ++i)
{
for (j = 1; j <= pow(2, i - 1); j++)
{//只要二叉树的深度确定,其结点在二叉数组中的横纵坐标都可以确定
m = (2 * j - 1)*pow(2, row - i);//根结点在二叉数组中的列位置
l = (4 * j - 3)*pow(2, row - i - 1);//左孩子在二叉数组中的列位置
r = (4 * j - 1)*pow(2, row - i - 1);//有孩子在二叉数组中的列位置
if (i == 1)
a[i][m] = *T;
if (a[i][m].lChild)
a[i + 1][l] = *(a[i][m].lChild);
if (a[i][m].rChild)
a[i + 1][r] = *(a[i][m].rChild);
}
}
for (i = 1; i <= row; ++i)
{
for (j = 1; j <= col; ++j)
{
if (a[i][j].data)
printf("%c", a[i][j].data);
else
printf(" ");
}
printf("\n");
}
}
else
{
printf("THIS IS A EMPTY BINARY TREE\n");
}
}
int main()
{
BiTree T = createBiTree();
/* ABC..DE.G..F... */
//CreateBiTree(T);
printf("%d", BiTreeDepth(T));
PreOrderTraverse(T,Visit);
printf("\n");
PrintBiTree(T, Visit);
cout << endl;
F_PostOrderTraverse(T);
/*
BiTree p = NULL;
p = (BiTree)malloc(sizeof(BiTNode));
p->data = 'P';
p->lChild = p->rChild = NULL;
InsertBiTree(T, 'B', p, 1);
DeleteBiTree(T, 'B', 1);
printf("\n");
PrintBiTree(T, Visit);
*/
return 0;
}
线索二叉树
n个结点的二叉树,有2n个指针域,使用的有n-1个,剩n+1个指针域浪费,可以用来存储线索。
指向前驱和后继的指针称作线索,加上线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded Binary Tree)
将二叉树以某种次序遍历使其变为线索二叉树的过程称作是线索化
指向前驱和后继的结点加起来一共是11,在加上指向孩子的指针域,一共20,充分利用了空间。
但为了清楚地确定lchild域指向的是前驱还是左孩子,需设两个标志域【boolean类型】
LTag为0时指向该结点的左孩子,为1时指向该结点的前驱
RTag为0时指向该结点的右孩子,为1时指向该结点的后继。
/*二叉树的二叉线索存储结构的定义*/
typedef enum{Link,Thread} PointerTag;/*Link表示指向左右孩子的指针,
Thread表示指向前驱或后继的线索*/
typedef struct BiThrNode
{
TElemType data;/*结点数据*/
struct BiThrNode *lchild,*rchild;/*左右孩子指针*/
PointerTag LTag;
PointerTag RTag;/*左右标志*/
}BiThrNode,*BiThrTree;
线索化的实质是将二叉链表中的空指针改为指向前驱或后继的线索的过程。
线索化的过程就是遍历的过程中修改空指针的过程。
BiThrTree pre;/*全局变量,始终指向刚刚访问过的结点*/
Status InThreading(BiThrTreee p)
{
if(p)
{
InThreading(p->lchild);//递归左子树线索化
if(!p->lchild)
{//没有左孩子
p->LTag = Thread;//前驱线索
p->lchild = pre;
}
if(!pre->rchild)
{//前驱没有右孩子
pre->RTag = Thread;//后继线索
pre->rchild = p;//前驱右孩子指针指向后继
}
pre = p;//保持pre指向p(下一个)的前驱
InThread(p->rchild);//递归右子树线索化
}
}
若二叉树不为空,在二叉链表上添加一个头结点,并令其lchild域的指针指向二叉树的根结点,其rchild域的指针指向中序遍历的最后一个结点。
若二叉树为空,令其lchild域和rchild域的指针都指向头结点。
这样做,我们既可以从第一个结点顺后继进行遍历,也可以从最后一个结点顺前驱进行遍历。