数据结构二叉树遍历总结⭐⭐⭐

DS-第五章-二叉树的遍历⭐⭐⭐

  • 数据结构二叉树遍历总结⭐⭐⭐
  • 二叉树的概念
  • 遍历
  • 习题分析与代码
  • 二叉树的遍历与线索二叉树
    • 先序遍历
      • 递归先序遍历
      • 非递归算法
    • 中序遍历
      • 递归中序遍历
      • 非递归算法
    • 后序遍历⭐
      • 递归算法
      • 非递归后序遍历算法⭐⭐(课后大题频出)
      • 非递归后序遍历与递归后序遍历的对比
  • 5.3.3习题分析与总结
    • 选择题
    • 综合大题

二叉树的概念

1、特点是:每个节点至多有两颗子树,分别为左子树和右子树
2、满二叉树:i<= ⌊n/2⌋,向下取整,i前面都是非叶子节点、i后面都是叶子节点
①如果n为奇数,则每个非叶子节点都有左右孩子,因为根节点只有一个,n-1个为偶数,则其余的层都是2的倍数
②如果n为偶数,则除了i= ⌊n/2⌋的非叶子节点只有左孩子外,其他非叶子都有左右孩子
③满二叉树是特殊的完全二叉树
④高度为h的满二叉树,利用等比数列求和公式可得,有2h-1个节点
扩展:
①高度为h的m叉数最多的节点数:N=(mh-1)/(m-1)
②高度为h的m叉数至少有h个节点(每个节点只有一个孩子)
③高度为h,度为m的树至少有h+m-1个节点。先构建高度为h的树,至少有h个节点,每一层一个节点,然后在任何一个节点再加m-1个节点,即可构建
④非空二叉树重要公式:N0=N2+1
如何用满二叉树的性质+先序序列pre得出,后序序列post呢?
3、完全二叉树

遍历

存储方式:顺序?/链式? 定义其数据结构类型?
顺序存储:

typedef struct SeqTree{int T[MaxSize];//容量及其大int length;//长度
}SeqTree

链式存储:

typedef struct BTNode{int data;//数据域struct BTNode *lchild;//左孩子int weight;//权重,WPL 王道P142 T19 2014年统考真题struct BTNode *rchild;//右孩子
}

习题分析与代码

1、 P120 T7

节点个数等于度数之和+1
原理是每一个非叶子节点的分支的指向都是一个节点,而每一个节点的分支树即为该节点的度数(出度),那么把所有节点的度数加起来,就是所有的分支数,但是不要忘了根节点是没有计上的,计上的只是根节点的出度(分支数),因此节点数=节点个数等于度数之和+1
20x4+10x3+1x2+10x1+1=123
n4+n3+n2+n1+n0=123 so n0=82
正确答案:B


2、P126 T4


因为只有度为0和度为2的节点,那么为了尽可能的少,只能是除了根节点外,每层2个节点,那么高度为h,所有至少有2*h-1个节点


3、P126 T6

最特殊的解法:
完全二叉树也是二叉树,满足性质,2n个节点,在完全二叉树中只有1个节点度数为1,即只能是奇数,所以C是错的
A:n0=n      n2=n-1      n0=1    n0+n2+n1=n   A√
B:n0=2m   n2=2m-1   n1=1   n0+n1+n2=4m,当4m=n时满足条件  B√
D:n2=2m   n0=2m+1   n1=1  n0+n1+n2=4m+2=n,当m=(n-2)/4满足条件  D√


4、P126 T16

该题中124为偶数的完全二叉树,n1=1
n0=n2+1   n2=123
sum=123+124+1=248


5、P126 T11

第6层有8个叶子节点,那么第6层就有25-8=24非叶子节点
那么第7层就有24*2=48个叶子节点
因为是完全二叉树,所以前6层共26-1=63
63+48=111
那么如果改一下题目,把最多改成最少呢?

如此就能得到8个叶子节点,且节点数量最少了。
第6层8个叶子节点
前5层共25-1=31
所以共31+8=39
总结规律:
①完全二叉树如果题目中已经说明了一层有多少个叶子节点,那么另一层的叶子节点也会确定
②要得到最少的节点数,且题目中已经说明了一层有多少个叶子节点,那么那层的叶子节点就放在前面
③要得到最多的节点数,且题目中已经说明了一层有多少个叶子节点,那么那层的叶子节点就放在后面,使其下一层还有叶子节点,下一层的叶子节点树=(本层的可拥有的最多节点数-本层叶子节点树)*2
要注意与没有说本层节点的叶子节点的只给出一共有多少个叶子节点,问一共有多个个节点的题型区分,如:T16与T11,因此一起总结.

由图可得,当上一层有一个度为1的节点时,完全二叉树的叶子节点树没有变化,而这就导致了相比于第一种情况而已多了一个节点
①当出现第一种情况时,除了最后一个节点外,其他节点都是度为2的节点,那么就有奇数个节点,因为根节点是要计上的
②而当出现第二种情况的时候:除了最后一层最右边的一个节点是多出来的,刚好与根节点相加=2,那么就和其他非叶子节点(度都为2)相加得到总节点数偶数
由此可得
①当节点总数为偶数时N1=1
②当节点总数为奇数时N1=0


6、P126 T20


首先先理解题目中树对应的二叉树无右孩子结点:意思就说,对应树中无右兄弟
法一:极端情况,叶子节点全在最后一层,那么最后一层的最后一个叶子节点无右兄弟,上面的1895个节点都只有孩子,没有兄弟,共1895+1=1896个

法二:通过二叉树的性质来做,116个叶子节点,N2=N0-1=116-1=115个度为2的节点,只有左孩子,或左右孩子都没有的节点,2011-115=1896个


非空完全二叉树,叶子节点都在同一层且非叶子节点都有两个子节点,就是满二叉树的性质
K个叶子结点那么非叶子结点就是K-1个,共2K-1


7、P127 T23

其实这里是有坑的,10个节点的二叉树在满足高度为5的的情况下,可能只需要16个存储单元,但是第5层的结点只能是一个放在首部,但不满足二叉树高度为5的任意性,其实那10个结点就是用来坑人的,关键信息还是高度为5,二叉树转化为顺序存储至少需要25-1=31个结点
那么为什么说10个结点是坑人的呢?,因为如果这10个节点的二叉树中第五层有一个节点再最右边的情况,即31号节点,那么如果采用最少使用的情况16个的话,将不能满足任意的高度为5的二叉树转化为顺序存储的二叉树。

二叉树的遍历与线索二叉树

先序遍历

递归先序遍历

先序遍历顺序:  根左右

void PreOrder(BiTree T){if(T){//非空条件visit(T);//访问PreOrder(T->lchild);//左孩子PreOrder(T->lchild);//右孩子}
}

非递归算法

void PreOrder(BiTree T){Stack S;//栈InitStack(S);//栈的初始化BiTree p;//辅助指针while(IsEmpty(S)||p!=NULL){if(p){//非空visit(p);//访问Push(S,p);//压栈p=p->lchild;//继续访问其左孩子,直到出现空指针}else{//为空,则把其双亲结点出栈Pop(S,p);//栈顶元素出栈并放入p指针中p=p->rchild;//访问右孩子}}
}

中序遍历

递归中序遍历

中序遍历顺序:  左根右

void InOrder(BiTree T){if(T){//非空条件PreOrder(T->lchild);//左孩子visit(T);//访问PreOrder(T->lchild);//右孩子}
}

非递归算法

void InOrder(BiTree T){Stack S;//栈InitStack(S);//栈的初始化BiTree p;//辅助指针while(IsEmpty(S)||p!=NULL){if(p){//非空Push(S,p);//压栈p=p->lchild;//继续访问其左孩子,直到出现空指针}else{//为空,则把其双亲结点出栈Pop(S,p);//栈顶元素出栈并放入p指针中visit(p);//访问p=p->rchild;//访问右孩子}}
}

后序遍历⭐

递归算法

后序遍历顺序:  左右根

void PostOrder(BiTree T){if(T){//非空条件PreOrder(T->lchild);//左孩子PreOrder(T->lchild);//右孩子visit(p);//访问}
}

非递归后序遍历算法⭐⭐(课后大题频出)

typedef struct Stack{BiTree t;//二叉树指针int tag;//标记,tag=0访问了左子树,tag=1访问右子树
}
int top;//栈顶指针
void PostOrder(BiTree T){Stack S[MaxSize];//栈,MaxSize足够大的容量top=0;while(T||top>0){//结点存在或者栈非空while(T){//一直循环,直至访问到左孩子为空S[++top].t=T;//入栈S[top].tag=0;//访问左结点T=T->lchild;//访问左结点}while(S[top].tag==1&&top>0){visit(S[top].t);//访问top--;//该结点已经访问过了就出栈!}if(top>0){//上一个while循环结束后top依然>0//说明还有结点的右孩子未被访问//只有当左右孩子都访问完了,才能访问其双亲结点S[top].tag=1;T=S[top].t->rchild;//遍历其右孩子}}
}

非递归后序遍历与递归后序遍历的对比

#include<stdio.h>
#include<stdlib.h>
#define MaxSize 10000
typedef struct BTnode{int  data;struct BTnode *lchild;//左孩子struct BTnode *rchild;//右孩子
}BTnode,*BiTree;
typedef struct Stack{BiTree t;//二叉树指针int tag;//标记,tag=0访问了左子树,tag=1访问右子树
}Stack;
int top;//栈顶指针
void visit(BiTree t){printf("%d ",t->data);
}
void PostOrder1(BiTree T){if(T){PostOrder1(T->lchild);PostOrder1(T->rchild);visit(T);}
}
void PostOrder(BiTree T){Stack S[MaxSize];//栈,MaxSize足够大的容量top=0;while(T||top>0){//结点存在或者栈非空while(T){//一直循环,直至访问到左孩子为空S[++top].t=T;//入栈S[top].tag=0;//访问左结点T=T->lchild;//访问左结点}while(S[top].tag==1&&top>0){visit(S[top].t);//访问top--;//该结点已经访问过了就出栈!}if(top>0){//上一个while循环结束后top依然>0//说明还有结点的右孩子未被访问//只有当左右孩子都访问完了,才能访问其双亲结点S[top].tag=1;T=S[top].t->rchild;//遍历其右孩子}}}
int main(){ BiTree T7=(BiTree)malloc(sizeof(BTnode));T7->data=7;BiTree T3=(BiTree)malloc(sizeof(BTnode));T3->data=3;T7->lchild=T3;BiTree T6=(BiTree)malloc(sizeof(BTnode));T6->data=6;T7->rchild=T6;BiTree T1=(BiTree)malloc(sizeof(BTnode));T1->data=1;T3->lchild=T1;T1->lchild=NULL;T1->rchild=NULL;BiTree T2=(BiTree)malloc(sizeof(BTnode));T2->data=2;T3->rchild=T2;T2->lchild=NULL;T2->rchild=NULL;BiTree T4=(BiTree)malloc(sizeof(BTnode));T4->data=4;T6->lchild=T4;T4->lchild=NULL;T4->rchild=NULL;BiTree T5=(BiTree)malloc(sizeof(BTnode));T5->data=5;T6->rchild=T5;T5->lchild=NULL;T5->rchild=NULL;printf("递归后序遍历算法:");PostOrder1(T7);printf("\n"); printf("非递归后序遍历算法:");PostOrder(T7);
}

5.3.3习题分析与总结

选择题

1、P138 T1


该图中:
中序遍历为:2 1
先序遍历为:1 2
A、B首先排除

图1

图2 由这两个图可看出,图2 的

先序序列为:1 2 3 4
中序为:2 1 4 3 所以D不满足D错误

若一个叶子结点为中序遍历的最后一个结点,说明这个结点在图的最右方,且没有左孩子和右孩子,那么先序遍历时到达图的最右方肯定当到这个没左右孩子的结点为最后一个结点,因此两者都在同一个结点停下


2、P139 T9

先序:根左右
后序:左右根
要序列相反有两种情况:
①每个结点只有右孩子
②每个结点只有左孩子
则每个结点度为1则会出现只有一个叶结点
所以正确答案为C


3、P140 T21

先序:根左右
中序:左根右
要想序列相同:
①每个结点只有右孩子->只有右子树
正确答案为B
但可能有人会选C,但是C只能保证度为1 的话,可能只有左孩子,可能只有右孩子,不能保证只有左孩子,这个是必要不充分条件


4、P140 T26

在先序序列中若左右子树非空,那么最后面的最后一个结点必定右空域,因为最后一个叶子结点只有一个前驱,而后继已经没有了只能NULL

而左子树为空的情况


5、P140 T28

线索二叉树考选择题,只要遍历一遍就直到如何的连接这些虚线了


举一反三


总结:
画线索二叉树首先先写出对应的遍历顺序(用于理清思路和验证线条)
根据遍历的过程,当左子树或者右子树为空时就要线索化。从输出的一个结点开始。


6、P141 T35

先序:根左右
后序:左右根
①每个结点只有右孩子
②每个结点只有左孩子

归纳总结为:
①要么度为1
②高度等于其结点数
答案选B

综合大题

1、P141 T3

法一:改变中序遍历+读栈不出栈法

void PostOrder(BiTree T){Stack S;//定义栈用于保存二叉树的结点InitStack(S);//栈初始化BiTree p=NULL;//用于判断是否是最近出过栈的结点while(T||!IsEmpty(S)){//结点非空/或者栈非空if(T){//结点非空Push(T);//结点入栈T=T->lchild;//一直向左走,直到为空为止}else{//最左端已经到底GetTop(S,T);//读栈顶元素if(T->rchild&&T->rchild!=p){//结点的右子树存在且未访问过T=T->rchild;//向右孩子走}else{//左子树已经走完,右子树又不存在或者已经访问过//即访问该节点Pop(S,T);//栈顶元素出栈visit(T);//访问该结点p=T;//更新当前最近访问过的结点,防止其双亲结点再判断到右孩子非空导致死循环T=NULL;//清除T指针内容,因为该指针的的左右子树包括其结点已经访问过出栈了}}}
}

法二:自定义栈的数据结构,含有tag标记,tag=0访问了左孩子,tag=1了有孩子,只有当左右孩子都访问了即tag=1了且栈中还有元素则访问该元素。

typdef struct Stack{BiTree t;//二叉树指针int tag;//tag=0 访问左孩子;tag=1 访问右孩子
}Stack;
int top;//栈顶指针
void PostOrder(BiTree T){Stack S[MaxSize];//MaxSize为足够大容量top=0;//栈顶指针while(T||top>0){//结点非空或栈非空while(T){//非空则一直向左S[++top]=T;//入栈S[top].tag=0;//访问左结点孩子T=T->lchild;//向左孩子走}while(S[top].tag==1&&top>0){//其孩子结点都已经访问,且栈中还有孩子visit(S[top].t);//访问该结点top--;//出栈}if(top>0){//经过上面的while循环后top还没有到达0,栈中还有元素//说明还有结点的右孩子还没被访问S[top].tag=1;//该节点记录走过T=S[top].t->rchild;//走向其右孩子继续访问}}
}

法三:先序 (左右顺序相反+栈输出)

void PostOrder2(BiTree T){//法三:先序 (左右顺序相反+栈输出) if(!T)return;BiTree S[MaxSize];//足够大容量用于遍历BiTree S1[MaxSize];//用于输出的栈int top=-1,top1=-1;//栈顶指针while(T||top>-1){while(T){S[++top]=T;//入栈S1[++top1]=T;//入栈T=T->rchild;//走向右孩子,顺序反向,先走右边}if(top>-1){//栈中还有结点T=S[top--];//取栈顶元素T=T->lchild;//走向左孩子}}//printf("%d %d\n",top1,top);while(top1!=-1){//输出visit(S1[top1--]);//访问}
}

2、P141 T5

此题为非递归算法,先引入递归算法求二叉树高度

int high(BiTree T,int h){//二叉树指针和记录当前的高度int lh,rh,max;//左右子树高度if(T){lh=high(T->lchild,h+1);//递归左子树rh=high(T->rchild,h+1);//递归右子树return ((lh>rh)?lh:rh)+1;}
}

非递归算法算二叉树高度
①层序遍历

int high(BiTree T){if(!T)//空树返回return -1;int front=-1,rear=-1;//队头队尾指针int last=0;//本层最后一个结点的指针int level=0;//高度BiTree Q[MaxSize];//队列Q[++rear]=T;//根节点入队BiTree p;//辅助指针用于保存出队结点while(front<rear){//队头指针小于队尾指针,证明还有元素p=Q[++front];//队头结点出队if(p->lchild)//左孩子存在Q[++rear]=p->lchild;//左孩子进队if(p->rchild)//右孩子存在Q[++rear]=p->rchild;//右孩子进队if(last==front){//队头指针已经走到了本层的最后一个结点的为止last=rear;//更新下一层的结点尾指针level++;//高度自增}   }return level;//返回高度
}

若我将这一题改成是求二叉树的宽度要怎么搞?
方法很简单,在上一题的非递归层序遍历上加上记录每一层的最大结点总数即可

int width(BiTree T){//求宽度if(!T)//空树返回return -1;int front=-1,rear=-1;//队头队尾指针int last=0;//本层最后一个结点的指针int count=0;//记录每一层的结点数int max=0;//保存最大宽度BiTree Q[MaxSize];//队列Q[++rear]=T;//根节点入队BiTree p;//辅助指针用于保存出队结点while(front<rear){//队头指针小于队尾指针,证明还有元素p=Q[++front];//队头结点出队count++;//本层节点数自增if(p->lchild)//左孩子存在Q[++rear]=p->lchild;//左孩子进队if(p->rchild)//右孩子存在Q[++rear]=p->rchild;//右孩子进队if(last==front){//队头指针已经走到了本层的最后一个结点的为止last=rear;//更新下一层的结点尾指针max=(max>count)?max:count;count=0;//每一层结束时,计数清0} }return max;//返回最大宽度
}

3、P141 T6

//采用递归算法建立二叉树
BiTree create(ElemType *xian,ElemType *zhong,int len){int i;//用于遍历中序序列找到左右子树的指针if(len==0)return NULL;//空树BiTree temp=(BiTree)malloc(sizeof(BTnode));//创建指针temp->data=*xian;//取值赋值for(int i=0;i<len;i++){if(*(zhong+i)==*xian)//如果遇到两者的值相等则break,记录此位置用于递归break;}temp->lchild=create(xian+1,zhong,i);//递归左子树temp->rchild=create(xian+i+1,zhong+i+1,len-i-1);//递归右子树return temp;
}


4、P141 T7

通过队列来判断二叉树是否为完全二叉树

bool IsComTree(BiTree T){//判断是否为完全二叉树if(!T)return false;//空树BiTree Q[MaxSize];//足够大的容量int front=-1,rear=-1;//队头队尾指针Q[++rear]=T;//根结点入队BiTree p;//辅助指针用于保存出队的结点while(front<rear){//队头指针小于队尾指针p=Q[++front];//队头结点出队if(p){//非NULL结点//把左右孩子都入队Q[++front]=p->lchild;Q[++front]=p->rchild;}else{//如果遇到叶子结点,那么后面就不能再有结点了,否则不是完全二叉树while(front<rear){//循环退队p=Q[rear--];//出队if(p)//居然还有非空结点return false;//不是完全二叉树}}}return true;//是完全二叉树
}

5、P141 T10

采用非递归先序遍历算法

int PreOrderFind(BiTree T,int k){int top=-1;//栈顶指针BiTree Stack[MaxSize];//MaxSize容量足够大int i=0;//用于与k比较while(top>-1||T){//当栈中有结点或者当前访问的结点非空if(T){i++;if(i==k)//到达第k个元素return T->data;//返回其结点的值Stack[++top]=T;//入栈T=T->lchild;//一直向左走}else{//当遇到空结点时,返回其双亲结点,向右走T=Stack[top--];//栈顶结点出栈T=T->rchild;//向右走}}return -1;//没有找到第k个值
}

采用递归写法

int flag=0;//标记是否找到第k个节点
int i=0;//编号
void PreOrderFind1(BiTree T,int k){//先序序列递归算法找第k个节点的值if(T){//非空条件i++;//自增第i个节点if(i==k){visit(T);flag=1;return; }if(flag!=1){PreOrderFind1(T->lchild,k);//左孩子PreOrderFind1(T->rchild,k);//右孩子}elsereturn;}
}

采用递归算法实现

int flag=0;
void PostOrder(BiTree T,int x){if(!T)//空树return;if(T->data==x){//找到x值的节点flag=1;//标记return;//返回}if(flag==0){PostOrder(T->lchild,x);//递归左子树}if(flag==0){PostOrder(T->lchild,x);//递归左子树}if(flag!=0){//找到了x了visit(T);}
}

采用非递归后序遍历算法找--------自定义栈算法—标记

typedef Stack{BiTree t;//指针域int tag;//tag=0 访问了左结点,tag=1,访问了右节点
}Stack;
void PostOrderFindX1(BiTree T,int x){//找x的所有祖先---自定义栈算法---标记if(!T)//空树return;Stack S[MaxSize];//栈,足够大容量int top=-1;//栈顶while(top>-1||T){//树非空或者栈中有节点while(T){//非空一直向左走S[++top].t=T;//入栈S[top].tag=0;//右节点已经访问T=T->lchild;//向左走}if(S[top].t->data==x){for(int i=top-1;i>=0;i--)visit(S[i].t);break;} //退出while,意味着遇到了空节点while(S[top].tag==1&&top>-1){top--;//已经访问过就退栈}if(top>-1){//经过上层的while循环,top还>-1//说明还有节点的右孩子未被访问S[top].tag=1;//标记该节点T=S[top].t->rchild;//访问右孩子}}
}

采用非递归后序遍历算法找----先序(顺序相反)+栈输出(有问题)

void PostOrderFindAn(BiTree T,int x){int top=-1,top1=-1;//双栈写法,一栈用于遍历,一栈用于输出BiTree S[MaxSize];//遍历栈BiTree S1[MaxSize];//输出栈while(top>-1||T){//栈中还有节点,或者树非空while(T){S[++top]=T;//入栈S1[++top1]=T;//入栈T=T->rchild;//先向右走//因为后序遍历右节点比左结点慢访问,子树根节点也比右节点慢访问//次序为 左-》右-》根//而根据栈的特性,先入栈的就后访问//因此,子树根节点入栈后,先走右孩子让右孩子入栈}if(S1[top1]->data==x){//找到x的位置for(int i=top-1;i>=0;i--)visit(S1[i]);exit(1);}if(top>-1){//栈中还有节点T=S[top--];//出栈取栈顶结点T->lchild;//访问左孩子}}
}

typedef Stack{BiTree t;//指针域int tag;//tag=0 访问了左结点,tag=1,访问了右节点
}Stack;
//ROOT根节点  p q任意两个节点
//ROOT根节点  p q任意两个节点
BiTree ANCESTOR(BiTree ROOT,BiTree p,BiTree q){//后序非递归算公共祖先Stack S[MaxSize];//栈,足够大容量Stack S1[MaxSize];//栈,足够大容量,用于保存对比指针int top=-1;//栈顶指针int top1=-1;//对比栈的栈顶BiTree T=ROOT;//辅助指针while(T||top>-1){while(T){//如果节点存在S[++top].t=T;//入栈S[top].tag=0;//记录访问左子树T=T->lchild;//向左走}while(top>-1&&S[top].tag==1){//左右子树都已经访问过了if(p==S[top].t){//找到p的位置for(int i=0;i<=top;i++)S1[i].t=S[i].t;//转移对应的数据到另一栈中,用于比较top1=top;//赋值栈顶}if(q==S[top].t){//找到q的位置,则开始比较for(int i = top;i>=0;i--){//从top向后找,因为要找到最近的公共节点for(int j=top1;j>=0;j--){if(S[i].t==S1[j].t)//有公共节点,则返回return S[i].t;//返回指针}}}top--;//出栈}if(top>-1){//经过上一面的while循环,当top还>-1时,说明还有节点的右孩子未被访问S[top].tag=1;//标记访问右孩子T=S[top].t->rchild;//取出栈顶元素}}return NULL;
}

有一个疑问:这个if(pS[top].t)和这个if(qS[top].t)放在while里面和外面有什么区别呢?

BiTree ANCESTOR1(BiTree ROOT,BiTree p,BiTree q){//后序非递归算公共祖先Stack S[MaxSize];//栈,足够大容量Stack S1[MaxSize];//栈,足够大容量,用于保存对比指针int top=-1;//栈顶指针int top1=-1;//对比栈的栈顶BiTree T=ROOT;//辅助指针while(T||top>-1){if(p==S[top].t){//找到p的位置for(int i=0;i<=top;i++)S1[i].t=S[i].t;//转移对应的数据到另一栈中,用于比较top1=top;//赋值栈顶}if(q==S[top].t){//找到q的位置,则开始比较for(int i = top;i>=0;i--){for(int j=top1;j>=0;j--){if(S[i].t==S1[j].t)//有公共节点,则返回return S[i].t;//返回指针}}}while(T){//如果节点存在S[++top].t=T;//入栈S[top].tag=0;//记录访问左子树T=T->lchild;//向左走}while(top>-1&&S[top].tag==1){//左右子树都已经访问过了top--;//出栈}if(top>-1){//经过上一面的while循环,当top还>-1时,说明还有节点的右孩子未被访问S[top].tag=1;//标记访问右孩子T=S[top].t->rchild;//取出栈顶元素}}return NULL;
}

两者的运行结果如下
经过多次不同数据的测试都是相同的
那么这两者有什么区别呢?


P142 T17

法一:(这是我采用的方法)采用层序遍历的方法比较两颗树,只有当两颗树每次遍历的结果都是要么都是空节点,要么都有左孩子或者都有右孩子,或者左右都有才会继续执行,否则返回false

bool LevelOrder(BiTree T1,BiTree T2){//层序遍历,识别两棵树是否相似
//当棵树都非空,或者两颗树都为空,时就继续执行,否则返回false,不相似if(!((T1&&T2)||(T1==NULL&&T2==NULL)))return false;if(T1==NULL&&T2==NULL)//两者都为空直接返回true 相似return true;BiTree Q1[MaxSize];//树1的队列int front1=-1,rear1=-1;//树1的队头队尾指针BiTree Q2[MaxSize];//树2的队列int front2=-1,rear2=-1;//树2的队头队尾指针BiTree p1,p2;//辅助指针,保存队列出队的结点Q1[++rear1]=T1;//树1的根结点入队Q2[++rear2]=T2;//树2的根结点入队while(front1<rear&&front2<rear2){p1=Q1[++front1];//树1队列队头出队p2=Q2[++front2];//树1队列队头出队if(p1->lchild&&p2->lchild){//左孩子都有//则左孩子都入队Q1[++rear1]=p1->lchild;//树1的左孩子入队Q2[++rear2]=p2->lchild;//树2的左孩子入队}//两者都为空,不做任何处理,默认为相似else if(T1->lchild||T2->lchild)//只要两者有一个有左孩子,就是不相似          return false;if(p1->rchild&&p2->rchild){//左孩子都有//则右孩子都入队Q1[++rear1]=p1->rchild;//树1的右孩子入队Q2[++rear2]=p2->rchild;//树2的右孩子入队}//两者都为空,不做任何处理,默认为相似else if(T1->rchild||T2->rchild)//只要两者有一个有右孩子,就是不相似          return false;}return true;//最后都处理完了还没返回flase 就是true了
}

运行结果如下:

法二:递归算法

int Similar(BiTree T1,BiTree T2){int LeftS,RightS;//左右子树是否相似if(T1==NULL&&T2==NULL)//两树皆空return 1;else if(T1==NULL||T2==NULL)//两树有一树为空return 0;else{//两树都存在LeftS=Similar(T1->lchild,T2->lchild);//递归左子树RightS=Similar(T1->rchild,T2->rchild);//递归左子树return LeftS&&RightS;//左右都为相似,才是相似}
}

比较结果(两种不同的数据)


P142 T19

统考真题,一题多解

我所采用的是层序遍历
思想:通过层序遍历,用一个变量sum记录总的WPL,记录当前的深度/高度,每次都判断该结点是否是叶子结点,如果是叶子结点则把其权值和深度相乘加入sum中,如果不是则继续层序遍历。

int LevelOrderWPL(BiTree T){if(!T)return 0;//空树int sum=0;//记录WPL总和int front=-1,rear=-1;//队头队尾指针int last=0;//本层的最后一个结点的指针,而根节点在0号索引,因此last初始为0int level=0;//深度BiTree Q[MaxSize];//队列Q[++rear]=T;//根节点入队BiTree p;//辅助指针用于保存出队结点while(front<rear){//队头指针小于队尾指针,还有结点p=Q[++front];//队头结点出队if(p->lchild)//有左孩子Q[++rear]=p->lchild;//左孩子入队if(p->rchild)//有右孩子Q[++rear]=p->rchild;//右孩子入队if(p->lchild==NULL&&p->rchild==NULL)sum+=(level)*p->weight;//累加if(front==last){//到达本层的最后一个结点last=rear;//更新为下一层的尾指针level++;//深度自增}}return sum;//返回WPL
}

法二------先序遍历

int WPL=0;//全局遍历WPL变量
int PreOrderWPL(BiTree T,int h){//当前的深度if(T->lchild==NULL&&T->rchild==NULL)//叶子结点WPL+=T->weight*h;//累加if(T->lchild)PreOrderWPL(T->lchild,h+1);//递归左孩子if(T->rchild)PreOrderWPL(T->rchild,h+1);//递归右孩子return WPL;//返回变量
}

P143 T20

法1:我写的时候的方法是:采用中序遍历递归算法+全局遍历用于判断是否要加括号,如果第一次遇到操作符,不加括号,若不是叶子结点且不是第一次遇到操作符,访问左孩子前加"(",访问右孩子后加")",若是叶子结点则输出操作数

void InOrder(OpTree T){if(!T)return;//空则返回if(T->lchild==NULL&&T->rchild==NULL)//叶子结点printf("%c",T->data);else{//非叶子结点count++;if(count!=1)//不是第一次遇到操作符printf("(");if(T->lchild)InOrder(T->lchild);//递归左子树printf("%c",T->data);if(T->rchild)InOrder(T->rchild);//递归右子树if(count!=1)printf(")");count--;}
}

运行结果如下

冲鸭!!!兄弟们,加油!

DS-第五章-二叉树的遍历相关推荐

  1. 啊哈算法第五章 图的遍历

    一.图的一些概念 简单来说,图是由顶点和连接这些顶点的边构成的集合.遍历就是指把图的每一个顶点都访问一次,用一个数表示各个顶点被第几个访问到,这个数就叫时间戳. 图的邻接矩阵存储法: 关联于同一条边的 ...

  2. 王道408数据结构——第五章 树与二叉树

    文章目录 一.树的基本概念 树的性质 二.二叉树 满二叉树 完全二叉树 二叉排序树 平衡二叉树 二叉树的性质 完全二叉树的性质 三.二叉树的储存结构 顺序储存 链式存储 四.树的储存方式 双亲表示法 ...

  3. (王道408考研数据结构)第五章树-第三节1:二叉树遍历(先序、中序和后序)

    文章目录 一:二叉树遍历概述 二:二叉树深度优先遍历 (1)先序遍历-根左右(NLR) (2)中序遍历-左根右(LNR) (3)后序遍历-左右根(LRN) 总结:三种遍历方式动图演示 三:二叉树的层序 ...

  4. c++ 图的连通分量是什么_学习数据结构第五章:图(图的遍历操作)

    第五章:图(图的遍历操作) 1.图的遍历 图的遍历:从图中某一顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问依次且仅访问一次 其实树的层次遍历和图的广度优先搜索类似,可以把这个二叉树看成一 ...

  5. 数据结构笔记(王道考研) 第五章:树和二叉树

    大部分内容基于中国大学MOOC的2021考研数据结构课程所做的笔记,该课属于付费课程(不过盗版网盘资源也不难找...).后续又根据23年考研的大纲对内容做了一些调整,将二叉排序树和平衡二叉树的内容挪到 ...

  6. 【数据结构总结】第五章 树和二叉树(非线性结构)

    第五章 树和二叉树(非线性结构) 提示:本文主要是以思维导图的形式概括数据结构第一章的精华内容,基本不会用到文字性的内容,目的是为了给大家梳理每个重要的知识点的相关概念,方便大家在复盘的时候快速阅读和 ...

  7. 数据结构(C语言第2版) 课后习题答案之第五章 树和二叉树

    目录 第5章  树和二叉树 一.选择题 二.应用题 (1)试找出满足下列条件的二叉树 (2)设一棵二叉树的先序序列: A B D F C E G H ,中序序列: B F D A G E H C (3 ...

  8. 408数据结构考研笔记——第五章树与二叉树(重点)

    目录 一.基本概念 1.定义 2.基本术语 3.性质(重点!!) 二.二叉树 1.定义 2.特殊二叉树 1.满二叉树 2.完全二叉树 3.二叉排序树 4.平衡二叉树 3.性质 4.存储结构 1.顺序存 ...

  9. 第五期 C/C++数据结构 二叉树的遍历以及结点数、深度

    代码详见后面 实验三 树和二叉树 一.实验目的 1.使学生熟练掌握二叉树的逻辑结构和存储结构(重点). 2.熟练掌握二叉树的各种遍历算法(难点). 二.实验原理及说明 1. 前序遍历算法思想: (1) ...

最新文章

  1. python画图三维-Python使用matplotlib绘制三维图形示例
  2. 基于机器学习的捡球机器人设计与实现(探索)第2篇——7步完成opencv的安装(20190112)
  3. golang 位操作
  4. mpstat 命令查看所有CPU核信息
  5. Android 5.0及以上实现屏幕截图
  6. Java正则表达式简单用法
  7. 将一张表的主键(ID)重置为从1开始自增排列
  8. 2019 徐州icpc网络赛 E. XKC's basketball team
  9. android音频采集时延,有关音视频采样率帧率以及 Duration 的那些事
  10. sfc流程图怎么画_sfc第四次超级机器人大战流程图
  11. 对抗恶意程序的反虚拟化,百度安全提最新检测技术,具备三大特性
  12. Python字符的转义
  13. 【控制理论】离散及连续的LQR控制算法原理推导
  14. 数据库左连接、右连接、内连接、全连接笔记
  15. 计算机桌面保护时间,请教一个屏幕保护的问题,域内的计算机可以设置不同时间的屏幕保护策略吗?...
  16. 尚医通——后台搭建——MybatisPlus自动填充和乐观锁
  17. oracle表空间配额(quota)与UNLIMITED TABLESPACE系统权限
  18. 基于android的线上教育app
  19. 一文搞懂K-means聚类算法
  20. 不可多得的干货!互联网公司常用分库分表方案汇总!太完整了!

热门文章

  1. js发送post请求
  2. C++类大小详尽讲解
  3. IDEF1x语义建模方法及其在数据库设计中的应用
  4. Hi3518ev200:byun hawkeye刷机与配网
  5. 远光软件2020年报: 创新效能持续释放,营收利润稳健增长
  6. ​多卡多链路聚合路由器的浅析
  7. 手把手教你下载VS2022(超详细)
  8. 社区分享|中南民族大学基于JumpServer构建规范、便利的运维安全体系
  9. 联想笔记本K4350安装win7系统
  10. 从《头号玩家》说起,聊聊当前的 VR 技术到底差在哪?