目录
一、遍历模板
1、先序遍历模板
2、中序遍历模板
3、后序遍历模板
二、例题
1、表达式(a-(b+c))*(d/e)存储在图6-7所示的一棵以二叉链表为存储结构的二叉树中(二叉树结点的data域为字符型),编写程序求出该表达式的值(表达式中的操作数都是一位的整数)。
2、写一个算法求一棵二叉树的深度,二叉树以二叉链表为存储方式。
3、在一棵以二叉链表为存储结构的二叉树中,查找data域值等于key 的结点是否存在(找到任何一个满足要求的结点即可),如果存在,则将q指向该结点,否则q赋值为NULL,假设data为int型。
4、假设二叉树采用二叉链表存储结构存储,编写一个程序,输出先序遍历序列中第k 个结点的值,假设k 不大于总的结点数(结点data域类型为char型)。
//1、先序遍历
void preOrder(BTNode *p)
{
if(p!=NULL){
visit(p->data);
preOrder(p->lchild);
preOrder(p->rchild);
}
}
//2、中序遍历
void inOrder(BTNode *p)
{
if(p!=NULL){
preOrder(p->lchild);
visit(p->data);
preOrder(p->rchild);
}
}
//3、后序遍历
void postOrder(BTNode *p)
{
if(p!=NULL){
preOrder(p->lchild);
preOrder(p->rchild);
visit(p->data);
}
}
/*
[分析]
1、先分别算出左子树a、右子树的值b
2、很结点存储的是运算符*,将 a*b的结果返回
3、此处用后序遍历
*/
int comp(BTNode *p){
int A,B;
if(p!=null){
if(p->lchild!=NULL && p->rchild!=NULL){ //p要么有左右孩子,要么没有,没有左右孩子结点时,它是根节点,也就是最终计算的值
A=comp(p->lchild);
B=comp(p->rchild);
return op(A,B,p->data);
}else{
return p->data+'0'; //注意此处的写法,将字符转为数字
}
}else{
return 0;
}
}
/*
[分析]
1、一棵树,左子树的深度为LD,右子树的深度为RD,那么这棵树的深度就为max{LD,RD}+1
2、用递归 ,注意递归结束条件。
如本题,递归结束条件就是遍历到叶子节点,叶子节点的孩子传入递归函数,传入参数为空,此时直接返回0(叶子节点的子树的深度为0)
*/
int getDepth(BTNode *p){
int LD,LR;
if(p!=null){
LD=getDepth(p->lchild);
LR=getDepth(p->rcild);
return LD>LR ? LD+1 : LR+1;
}else{
return 0;
}
}
/*
[分析]
遍历二叉树,此处选择先序遍历
*/
void search(BTNode *p,BTNode *&q, int key){
if(p!=NULL) {
if(p->data == key){
q = p;
}else{
serach(p->lchild,q,key);
search(p->rchild,q,key);
}
}
}
//改进
void search(BTNode *p,BTNode *&q, int key){
if(p!=NULL) {
if(p->data == key){
q = p;
}else{
serach(p->lchild,q,key);
if(q==NULL) //加了这一行
search(p->rchild,q,key);
}
}
}
/*
[分析]
遍历二叉树,此处选择先序遍历 。灵活运用遍历模板
*/
int n=0;
void getValue(BTNode *p,int k){
if(p!=NULL){
++n; //注意++n的位置,先加n,再与k比较
if(k==n)
{
cout<data<child,k);
getValue(p->rchild,k);
}
}
//将题目改成中序
int n=0;
void getValueIn(BTNode *p,int k){
if(p!=NULL){
getValueIn(p->lchild,k);
n++;
if(k==n){
cout<data<rchild,k);
}
}
//将题目改成后序
int n=0;
void getValuePost(BTNode *p,int k){
if(p!=NULL){
getValuePost(p->lchild,k);
getValuePost(p->rchild,k);
n++;
if(k==n){
cout<data<
图6-10所示为二叉树的层次遍历,即按照箭头所指方向,按照1、2、3、4的层次顺序,对二叉树中各个结点进行访问(此图反映的是自左至右的层次遍历,自右至左的方式类似)。
/*
【分析】
要进行层次遍历,需要建立一个循环队列。
先将二叉树头结点入队列,然后出队列,访问该结点,如果它有左子树,则将左子树的根结点入队;如果它有右子树,则将右子树的根结点入队。
然后出队列,对出队结点访问。如此反复,直到队列为空为止。 (记住最后一句话的说法)
【步骤】
1、定义并初始化一个循环队列
2、将二叉树的根节点入队
3、开始遍历列队。
出队并访问队头结点。
队头结点有左孩子:队头结点的左孩子入队;
堆头结点有右孩子:队头结点的右孩子入队;
*/
void levelRecurse(BTNode *p){
//1、定义并初始化一个循环队列
int front,rear;
BTNode *que[maxSize]; //maxSize为已定义好的变量,是队列的最大长度
front=rear=0; //初始化队列
BTNode *q;
if(p!=NULL){
//根结点入队
rear = (rear+1)%maxSize; //循环队列,入队出队时,千万别忘了取余MaxSize !!!
que[rear]=p;
while(front!=rear){ //当队列不为空时进行循环
//出队头元素
front = (front+1)%maxSize;
q=que[front];
visit(q->data); //访问队头元素
if(q->lchild !=NULL){ //左孩子不为空
//左孩子结点入队
rear =(rear+1)%maxSize;
que[rear]=q->lchild;
}
if(q->rchild !=NULL){ //右孩子不为空
//右孩子结点入队
rear =(rear+1)%maxSize;
que[rear]=q->rchild;
}
}
}
}
假设二叉树采用二叉链表存储结构存储,设计一个算法,求出该二叉树的宽度(具有结点数最多的那一层上的结点个数)。
/*
【分析】
前提:层次遍历的基础
第一点: 出队元素的左、右孩子结点的层次 = 出队元素的层数+1;
根节点的层数为1;
第二点: 知道了即将入队的结点的层数,那么可以在结点入队时,一起将入队结点的层数存进去。
第三点: 将循环队列改成非循环队列(前提是队列长度大于二叉树的结点个数),
这样对二叉树的结点遍历结束之后,二叉树的结点都存在队列中。
【步骤】
1、
*/
typedef struct St //定义队列中存储的元素的结构体,为结点+结点所在层数
{
BTNode *p;
int levelNum;
}St; //结构体定义,千万不要忘记最后的结尾!!!
int getWidthOfTree(BTNode *b){
//1、定义并初始化一个队列
St que[maxSize];
int front,rear;
front=rear=0;
BTNode *q;
int Lnum=0; //存放最大层数
if(b!=NULL){
//根节点入队,层数为1
rear++;
que[rear]->p=b;
que[rear]->levelNum=1;
while(front!=rear){
++front;
q=que[front]->p;
Lnum=que[front]->levelNum;
if(q->lchild!=NULL){ //出队结点的左孩子结点及其层数入队
++rear;
que[rear]->p=q->lchild;
que[rear]->levelNum=Lnum+1;
}
if(q->rchild!=NULL){ //出队结点的右孩子结点及其层数入队
++rear;
que[rear]->p=q->rchild;
que[rear]->levelNum=Lnum+1;
}
}//神来之笔!!!循环结束时,Lnum中存放的是二叉树的最大层数,方便后面遍历。
int max=0;//变量max存放最大宽度
int n; //变量n暂时存放每一层的结点个数
for(int i=1;i<=Lnum;++i){ //根据层数遍历
n=0;
for(int j=0;jlevelNum == i)
n++;
if(n>max)
max=n;
}
}
return max;
}else{
return 0;
}
}
/*
【分析】
非递归,用到栈。
【步骤】
1、定义一个栈
2、树的根结点入栈
3、树的根结点出栈
4、访问根结点
5、根结点存在右孩子,右孩子入栈
6、根结点存在左孩子,左孩子入栈
7、如此反复,直到栈空
*/
void preOrderNonRecursion(BTNode *bt){
if(bt !=NULL){
//1、定义并初始化一个栈,存放树节点
BTNode *Stack[maxSize];
int top=-1;
BTNode *p; //定义p存放出栈结点
//2、根节点入栈
Stack[++top]=bt;
while(top!=-1){
p=Stack[top--];
visit(p);
if(p->rchild!=NULL) //注意:此处是右孩子先入栈,因为先入后出,先入右孩子,后访问右孩子
Stack[++top]=p->rchild;
if(p->lchild!=NULL)
Stack[++top]=p->lchild;
} //循环结束,二叉树的遍历完成
}
}
/*
【分析】
非递归,用到栈。
结点出栈时访问结点,结点的右孩子是在访问出栈结点之后
【步骤】
1、根结点入栈
2、循环执行如下操作:如果栈顶结点左孩子存在,则左孩子入栈;
如果栈顶结点左孩子不存在,则出栈并输出栈顶结点,
然后检查其右孩子是否存在,如果存在,则右孩子入栈。
3、当栈空时,算法结束
*/
void InOrderNonRecursion(BTNode *bt){
if(bt!=NULL){
BTNode *Stack[maxSize];
int top=-1;
BTNode *p; //p存放的是即将进栈的结点
p=bt;
while(top!=-1 || p!=NULL){ //在遍历的过程中,可能会出现栈空的情况,所以不能只靠栈空来结束循环,
while(p!=NULL){ //即将入栈的结点p,入栈,并将p指向入栈结点的左孩子
Stack[++top]=p; //一路左到底,然后开始出栈访问操作
p=p->lchild;
}
if(top!=-1){ //当p为空时,也就是栈顶元素没有左孩子,栈顶元素出栈,访问栈顶元素,再将p指向出栈元素的右孩子
p=Stack[top--];
Visit(p); //访问完出栈元素之后,将p(即将进站的元素)指向出栈元素的右孩子
p=p->rchild;
}
}
}
}
先序遍历:根左右
后序遍历:左右根
逆后续遍历:根右左
后续非递归遍历就是:先对树进行根右左的遍历,然后将根左右的顺序放入到一个栈2中,再对栈2进行出栈遍历操作。