二叉树遍历变形:
(0)二叉树的层次遍历分层打印
(1)输出二叉树中所有的叶子节点
(2)释放二叉树中的所有节点
(3)基于递归的二叉树算法设计
(4)递归法求二叉树中的所有叶子节点
(5)查找值为x的节点,找到返回其指针
(6)二叉树的 高度
(7)求先序遍历中的第k个节点的值
(8)二叉树的叶子节点个数
(9)二叉树中的单分支节点个数
(10)双分支节点个数
(11)二叉树中值为k的节点个数
(12)判断一棵二叉树是否为满二叉树
(13)把二叉树b复制到二叉树t中
(14)把二叉树b交换左右子树后存到二叉树t中
(15)交换左右子树
(16)判断两棵树是否相似
(17)将二叉树的顺序存储结构转换为链式存储结构
(18)将二叉树的链式结构转换为顺序结构
(19)求二叉树中节点值为x的节点的层数
(20)求二叉树中距离p最近的叶子节点
(21)求二叉树的宽度(即节点个数最多的一层的节点个数)
(22)二叉树中指定某一层的叶子节点个数
(23)输出从每一个叶子节点到根节点的逆路径:(即从根节点到叶子节点的路径)
(24)输出二叉树中最长的一条路径
(25)输出值为x的节点的所有祖先
(26)输出从根节点到节点值为x的节点的路径
(27)求节点值为x和节点值为y的两个节点的最近共同祖先
(28)
-----------------------------------------
(0)二叉树的层次遍历分层打印
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
比如:
输出:
1
2 3
4 5 6
7 8
原始实现:
void PrintBFS(Node* root) {
queue<Node*> Q;
Q.push(root);
do {
Node *node = Q.front();
Q.pop();
cout << node->data << " ";
if (node->pLeft)
Q.push(node->pLeft);
if (node->pRight)
Q.push(node->pRight);
}
while (!Q.empty());
}
说明:难点在于如何换行,即如何每行输出;
(0.1)方法一:
利用了两个队列,队列A储存本层的节点,队列B储存下层的节点。
元素始终从队列A中出队,遍历本层的节点,把其子代节点排入队列B。
本层遍历完毕后,就可换行,并交换两个队列。
ps:队列A/B中只会存储一个行中的元素。一个队列中不会同一时间存在2个行中的元素。所以队列A出队完,即可打换行;
void PrintNodeByLevel(Node* root) {
deque<Node*> Q1, Q2;
Q1.push_back(root); //根节点进入队列1;
do {
do {
/*
一个队列Q1 会记录一层中的所有节点,不会有多个层中的节点同时出现在一个队列中;
所以,将Q1中所有的元素出队列,并访问,这样一层就访问完了,同时将该层的下一层的元素存入到队列Q2中;
*/
Node* node = Q1.front(); // 元素始终从队列1中出队;
Q1.pop_front();
cout << node->data << " ";
if (node->pLeft)
Q2.push_back(node->pLeft);
if (node->pRight)
Q2.push_back(node->pRight);
} while (!Q1.empty());
cout << endl; // Q1队列为空是换行;
Q1.swap(Q2); /*此中的swap操作其实是将队列2所有元素出队,加入到队列1中*/
} while(!Q1.empty());
}
(0.2)方法二:
把一个结束标识放进队列里。
当发现空指针(结束信号)时,要检查队列内是否还有节点,如果没有的话还插入新的结束信号,则会做成死循环。
void PrintNodeByLevel(Node* root) {
queue<Node*> Q;
Q.push(root); //根节点进队列;
Q.push(0); //第一层的结束标识;对于每一行来说,该行节点在前,结束标识在后。
do {
Node* node = Q.front();
Q.pop();
if (node) { //节点非空,说明是正常的节点;
cout << node->data << " ";
if (node->pLeft)
Q.push(node->pLeft);
if (node->pRight)
Q.push(node->pRight);
}
else if (!Q.empty()) {
/*遇到上一个行的结束标识,说明本行的节点都已经入队了。所以需要在本行加结束标识;*/
Q.push(0);
cout << endl;
}
} while (!Q.empty());
}
-------------
(1)输出二叉树中所有的叶子节点:
void printLeaves(BTNode* b){
if(b){
if(b->lchild==NULL && b->rchild==NULL){
visit(b->data);
}
printLeaves(b->lchild);
printLeaves(b->rchild);
}
}
注:基于先序遍历
-----
(2)释放二叉树中的所有节点:
void deleteBT(BTNode* b){
if(b){
deleteBT(b->lchild);
deleteBT(b->rchild);
free(b);
}
}
注:基于后序遍历
------
(3)基于递归的二叉树算法设计:
二叉树是递归的数据结构,所以一棵非空的二叉树的 左右子树也是二叉树。
1)设原问题为f(b),较小问题为f(b->lchild)和f(b->rchild);
2)假设f(b->lchild),f(b->rchild)是可求的,由此导出f(b);
3)b为空或只有一个节点(叶子节点)时,递归结束。
-------
(4)递归法求二叉树中的所有叶子节点:
1)递归模型如下:
当b=NULL, f(b)=不做任何事;
当b=叶子节点, f(b)=输出b->data的值;
其他情况 , f(b)=f(b->lchild);f(b->rchild);
void printLeaves(BTNode* b){
if(b){
if(b->lchild==NULL && b->rchild==NULL)
visit(b->data);
else{
printLeaves(b->lchild);
printLeaves(b->rchild);
}
}
}
--------
(5)查找值为x的节点,找到返回其指针,否则NULL;
1)递归模型:
当b=NULL; f(b,x)=NULL;
当b->data=x; f(b,x)=b;
其他情况; f(b,x)=f(b->lchild,x)或者f(b->rchild,x);
BTNode* findNode(BTNode* b, DataType x){
BTNode * pnode;
if(b==NULL) return NULL;
else if(b->data==x) return b;
else{
pnode=findNode(b->lchild,x);
if(pnode) return pnode;
else return findNode(b->rchild,x);
}
}
注:基于先序。
-------
(6)二叉树的 高度:
1)递归模型:
当b=NULL; f(b)=0;
当b=叶子; f(b)=1;
其他情况; f(b)=Max( f(b->lchild), f(b->rchild) )+1;
其实:b=叶子这种情况可以省略,已经包含在其他情况中。
int BTDepth(BTNodex* b){
int lchildDepth,rchildDepth;
if(b==NULL)
return 0;
else {
lchildDepth=BTDepth(b->lchild);
rchildDepth=BTDepth(b->rchild);
return (lchildDepth>rchildDepth)?(lchildDepth+1):(rchildDepth+1);
}
}
注:基于后序
--------
(7)求先序遍历中的第k个节点的值:
1)分析:用一个全局变量n记录访问过的节点个数,当二叉树为空时返回空格,当
n=k时,返回b->data,当n=k时,在左子树查找,若找到则返回该值,否则在右子树查找。
2)递归模型:
当b=NULL; f(b,k)=' ';返回空格
当n==k时; f(b,k)=b->data;
当n!=k时; f(b,k)=f(b->lchild,k)或者f(f->rchild,k);
3)代码:
int n=1;//初始时n=1,对应第一个节点,根节点。
DataType preNode_k(BTNode* b, int k){
if(b==NULL) return ' ';
if(n==k) return (b->data);
n++;
DataType tempData;
tempData=preNode_k(b->lchild,k);
if(tempData!=' ') return tempData;
else return preNode_k(b->rchild,k);
}
注:基于先序。
---------
(8)二叉树的叶子节点个数:
1)递归模型:
若b=NULL; f(b)=0;
若b为叶子; f(b)=1;
其他情况; f(b)=f(b->lchild)+f(b->rchild);
2)代码:
int leavfNodes(BTNode* b){
int num1,num2;
if(b==NULL) return 0;
else if(b->lchild==NULL && b->rchild==NULL) return 1;
else {
num1=leavfNodes(b->lchild);
num2=leavfNodes(b->rchild);
return (num1+num2);
}
}
注:基于后序。
-----------
(9)二叉树中的单分支节点个数:
1)递归模型:
当b=NULL; f(b)=0;
当b为单分支节点; f(b)=f(b->lchild)+f(b->rchild)+1;
当b为叶子或双分支;f(b)=f(b->lchild)+f(b->rchild);
int simpleSonNode(BTNode* b){
if(b==NULL) return 0;
else if( (b->lchild==NULL && b->rchild!=NULL) || (b->lchild!=NULL && b->rchild==NULL) )
//b为单分支
return simpleSonNode(b->lchild)+simpleSonNode(b->rchild)+1;
else return simpleSonNode(b->lchild)+simpleSonNode(b->rchild);
}
注:基于后序。
-----------
(10)双分支节点个数:
1)递归模型:
当b=NULL; f(b)=0;
当b为双分支; f(b)=f(b->lchild)+f(b->rchild)+1;
其他(b为叶子或单分支) f(b)=f(b->lchild)+f(b->rchild);
int doubleSonNode(BTNode * b){
if (b==NULL) return 0;
else if(b->lchild && b->rchild)
return doubleSonNode(b->lchild)+doubleSonNode(b->rchild)+1;
else return doubleSonNode(b->lchild)+doubleSonNode(b->rchild);
}
注:基于后序;
-------------
(11)二叉树中值为k的节点个数:
1)递归模型:
当b=NULL; f(b,k)=0;
当b->data=k; f(b,k)=1+f(b->lchild)+f(b->rchild);
当b->data!=k; f(b,k)=f(b->lchild)+f(b->rchild);
int count_k(BTNode* b, DataType k){
if(b==NULL) return 0;
else if(b->data==k)
return 1+count_k(b->lchild,k)+count_k(b->rchild,k);
else
return count_k(b->lchild,k)+count_k(b->rchild,k);
}
注:基于先序;
--------------
(12)判断一棵二叉树是否为满二叉树;
1)分析:满二叉树满足n=2^h-1,h为高度。
2)代码:
int height(BTNode* b){
int lchildDepth,rchildDepth;
if(b==NULL) return 0;
else {
lchildDepth=height(b->lchild);
rchildDepth=height(b->rchild);
return (lchildDepth>rchildDepth)?(lchildDepth+1):(rchildDepth+1);
}
}
int nodeCount(BTNode* b){
if(b==NULL) return 0;
else
return 1+nodeCount(b->lchild)+nodeCount(b->rchild);
}
int isFullBT(BTNode* b){
int count;
int h;
count=nodeCount(b);
h=height(b);
if( count==pow(2,n)-1 )
return 1;
else return 0;
}
----------
(13)把二叉树b复制到二叉树t中;
1)递归模型:
当b=NULL; f(b,t)中; t=NULL;
其他情况; f(b,t)中; 复制根节点b产生t节点,
f(b->lchild,t->lchild),
f(b->rchild,t->rchild);
2)代码实现:
void copy(BTNode* b ,BTNode* t){
if(b==NULL) t=NULL;
else {
t=(BTNode* )malloc(sizeof(BTNode));//复制根节点t
t->data=b->data;
copy(b—>lchild,t->lchild);
copy(b->rchild,t->rchild);
}
}
注:基于先序。
-------------
(14)把二叉树b交换左右子树后存到二叉树t中:
1)递归模型:
当b=NULL; f(b,t)中;t=NULL;
其他情况; f(b,t)中; 复制根节点b产生t节点,
f(b->lchild,t->rchild),
f(b->rchild,t->lchild);
2)代码实现:
void swap(BTNode* b, BTNode* t){
if(b==NULL) t=NULL;
else {
t=(BTNode* )malloc(sizeof(BTNode));
t->data=b->data;
swap(b->lchild,t->rchild);
swap(b->rchild,t->lchild);
}
}
注:基于先序;
-------------
(15)交换左右子树;
1)递归模型:
当b=NULL; f(b)中不做任何事;
其他情况; f(b)中:
f(b->lchild);
f(b->rchild);
将b的左右孩子互换;
2)代码实现:
void swap(BTNode* b){
BTNode* pnode;
if(b){
swap(b->lchild);//左子树交换
swap(b->rchild);//右子树交换
pnode=b->lchild;//保存左指针
b->lchild=b->rchild;//交换b的左右指针
b->rchild=p;
}
}
注:基友后序。
-------------
(16)判断两棵树是否相似;
1)分析:
t1,t2相似,指的是t1,t2都是空的树;或者t1,t2的根节点相似,且t1的左子树,右子树分别与t2的左子树,右子树相似。
2)递归模型:
t1,t2均为NULL时; f(t1,t2)=true;
t1,t2一个为NULL,另一个不是 f(t1,t2)=false;
其他情况 f(t1,t2)=f(t1->lchild,t2->lchild)&&f(t1->rchild,t2->rchild);
3)代码实现:
int isLike(BTNode* t1,BTNode* t2){
if(t1==NULL && t2==NULL) return 1;
else if( (t1==NULL && t2!=NULL) ||(t1!=NULL && t2==NULL) )
return 0;
else {
int like1,like2;
like1=isLike(t1->lchild,t2->lchild);
like2=isLike(t1->rchild,t2->rchild);
return (like2&&like1);
}
}
注:基于基于后序。
------------
(17)将二叉树的顺序存储结构转换为链式存储结构:
1)分析:
二叉树的顺序存储结构为BTNode* seTree[Maxsize],
f(seTree,i)来得到由seTree[i]创建的节点指针,显然f(seTree,0)得到的是根节点指针,
2)递归模型:
当i>=MaxSize; f(seTree,i)=NULL;
当seTree[i]='#'即i号为空 f(seTree,i)=NULL;
其他情况; f(seTree,i)=b,创建根节点b(值为seTree[i])
b->lchild=f(seTree,2*i+1);
b->rchild=f(seTree,2*i+2);
3)代码:
BTNode* seTree_to_linkTree(BTNode* seTree,int i){
BTNode* b;
if(i>=Maxsize) return NULL;
else if(seTree[i]=='#')//i号节点为空时
return NULL;
else {
b=(BTNode*) malloc(sizeof(BTNode));
b->data=seTree[i];
b->lchild=seTree_to_linkTree(seTree,2*i+1);
b->rchlld=seTree_to_linkTree(seTree,2*i+2);
return b;
}
}
或者:
void seTree_to_linkTree(BTNode* &b ,BTNode* seTree,int i ){
if(i>=Maxsize) b=NULL;
else if(seTree[i]='#')
b=NULL;
b=(BTNode*)malloc(sizeof(BTNode));//创建根节点
b->data=seTree[i];
seTree_to_linkTree(b->lchild,seTree,2*i+1);//创建左子树
seTree_to_linkTree(b->rchild,seTree,2*i+2);//创建右子树
}
注:基于先序。
---------------
(18)将二叉树的链式结构转换为顺序结构:
1)分析:二叉树的顺序存储结构a,(初始时所有元素的值为‘#’)
2)递归结构:
当b=NULL; f(a,b,i)不做任何事;
其他情况; f(a,b,i)中 a[i]=b->data;
f(a,b->lchild,2*i+1);
f(a,b->rchild,2*i+2);
3)代码实现:
void linkTree_to_SeTree(SqTree a ,BTNode* b,int i){
if(b){
a[i]=b->data;
linkTree_to_seTree(a,b->lchild,2*i+1);
linkTree_to_seTree(a,b->rchild,2*i+2);
}
}
注:基于先序、
------------
(19)求二叉树中节点值为x的节点的层数:
1)分析:
设f(b,x,h)等于data值为x的节点的层次,当找不到data值为x的节点时返回0;其中,h表示b所指节点的层次,初始时调用方式为f(b,x,1),即从根节点开始查找。
2)递归模型:
当b=NULL; f(b,x,h)=0;
当b->data==x; f(b,x,h)=h;
其他情况; f(b,x,h)=f(b->lchild,x,h+1)或f(b->rchild,x,h+1);
3)代码实现:
int level(BTNode* b, DataType x ,int h){
//h表示b所在层次。
if(b==NULL){
return 0;
}
else if(b->data==x){
return h;
}
else {
int level_left=level(b->lchild,x,h+1);
//b所在层次为h,所以b的左孩子所在的层次为h+1;
if(level_left)
return level_left;
else return level(b->rchild,x,h+1);
}
}
注:基于先序;
或者:
1)分析:
基于层次遍历,在队列中设置一个数组来保存进队节点所在的层次,在出队的 时候若当前节点
的data为x,就返回对应的层次,否则将不空的左孩子和不空的右孩子入队列。
2)代码实现:
int level(BTNode* b, DataType x){
struct {
int front,rear;
BTNode* dataArray[Maxsize];
int level[Maxsize];//保存各个节点的层次
}Qu;//定义结构体变量Qu;
Qu.front=Qu.rear=0;//队头队尾初始化
Qu.rear=(Qu.rear+1)%Maxsize;//根节点入队。
Qu.dataArry[Qu.rear]=b;
Qu.level[Qu.rear]=1;//根节点对应的层次为1;
while(Qu.rear!=Qu.front){
Qu.front=(Qu.front+1)%Maxsize;//出队列
BTNode* p=Qu.dataArray[Qu.front];
int h=Qu.level[Qu.front];
if(p->data==x)//关键码
return h;
if(p->lchild!=NULL){//p的左孩子进队
Qu.rear=(Qu.rear+1)%Maxsize;
Qu.dataArray[Qu.rear]=p->lchild;
Qu.level[Qu.rear]=h+1;
}
if(p->rchild!=NULL){//p的右孩子进队
Qu.rear=(Qu.rear+1)%Maxsize;
Qu.dataArray[Qu.rear]=p->rchild;
Qu.level[Qu.rear]=h+1;
}
}
return 0; //如果没有找到,则返回0
}
----------
(20)求二叉树中距离p最近的叶子节点;
1)分析:
采用层次遍历,从p节点开始遇到的第一个叶子节点就是所求。
2)代码如下:
DataType findLeave(BTNode* b , BTNode* p){
BTNode* Qu[Maxsize];
BTNode* pnode;
int front=0;
int rear=0;
rear=(rear+1)%Maxsize;//p节点进队
Qu[rear]=p;
while(front!=rear){
front=(front+1)%Maxsize;
pnode=Qu[front];
if(pnode->lchild==NULL && pnode->rchild==NULL){
return pnode->data;
}
if(NULL!=pnode->lchild){
rear=(rear+1)%Maxsize;
Qu[rear]=pnode->lchild;
}
if(NULL!=pnode->rchild){
rear=(rear+1)%Maxsize;
Qu[rear]=pnode->rchild;
}
}
}
-------
(21)求二叉树的宽度(即节点个数最多的一层的节点个数);
1)分析:
采用层次遍历,将各个节点和对应的层次放到一个队列中,通过扫描二叉树得到各个节点的层次。
2)代码实现:
int BTWidth(BTNode* b){
struct{
BTNode* pnodeArray[Maxsize];
int nodeLevel[Maxsize];
int front;
int rear;
}Qu;//定义结构体变量Qu;
Qu.front=Qu.rear=-1;//初始化front,rear;
Qu.rear++;
Qu.pnodeArray[Qu.rear]=b;
Qu.nodeLevel[Qu.rear]=1;
//根节点对应的下标是0,对应的层次是1;
BTNode* pnode;
int node_level;
while(Qu.rear!=Qu.front){
Qu.front++;//出队
pnode=Qu.pnodeArray[Qu.front];
node_level=Qu.nodeLevel[Qu.front];
if(NULL!=pnode->lchild){//左孩子进队
Qu.rear++;
Qu.pnodeArray[Qu.rear]=pnode->lchild;
Qu.nodeLevel[Qu.rear]=node_level+1;
}
if(NULL!=pnode->rchild){//右孩子进队
Qu.rear++;
Qu.pnodeArray[Qu.rear]=pnode->rchild;
Qu.nodeLevel[Qu.rear]=node_level+1;
}
}
printf("输出各个节点的层次\n");
for(int i=0;i<=Qu.rear;i++){
//根节点对应的是rear=0;所以rear+1为节点个数
printf("%c, %d \n",Qu.pnodeArray[i].data, Qu.nodeLevel[i]);
}
int MaxWidth=0;
int tempWidth;
int tempLevel=1;//从根节点开始,根节点的层次为1;
int i=0;//根节点对应的下标为0
while(i<=Qu.rear){
tempWidth=0;
while(i<=Qu.rear && Qu.nodeLevel[i]==tempLevel){
tempWidth++;
i++;
}
tempLevel=Qu.nodeLevel[i];
if(tempWidth>MaxWidth)
MaxWidth=tempWidth;
}
return MaxWidth;
}
注:队列中的节点,出队后仍然需要保留在队列中以便后来求二叉树的宽度,所以设置的 队列不是循环队列,如果用循环队列可能被其他的节点覆盖。
-------
(22)二叉树中指定某一层的叶子节点个数:
1)分析:
基于层次遍历,若某一层的节点如队后,rear指向该层中最右节点,然后用last来保存最右节点(对于第一层的最右节点即根节点,则last=1)。出队时,若front==last,表示这一层处理完毕,让层次level++;并设置last为下一层的最右节点;
因为第一层只有一个节点,所以根节点就是最右节点。对于其他层,上一层最右节点最后入队的孩子就是最右节点。
2)代码实现:
int leaves_Of_LevelK(BTNode* b,int k){
BTNode* Qu[Maxsize];
int front,rear;
int level;//记录层次;
int last;//逐层记录最右节点的编号。即最右节点是第几个节点,
int leaveCount;//叶子个数
front=rear=0;//初始化
if(b==NULL || k<=1) return 0;
rear++; //根节点入队
Qu[rear]=b;
last=rear; //初始时最右节点就是根节点,即第一个节点,编号为1.
level=1; //根节点对应的层次为1.
leaveCount=0;
BTNode* pnode;
while(rear!=front){
front=(front+1)%Maxsize;//出队
pnode=Qu[front];
if(level==k && pnode->lchild==NULL && pnode->rchild==NULL)
leaveCount++;
if(pnode->lchild){//左孩子进队
rear=(rear+1)%Maxsize;
Qu[rear]=pnode->lchild;
}
if(pnode->rchild){//右孩子进队
rear=(rear+1)%Maxsize;
Qu[rear]=pnode->rchild;
}
if(front==last){//当前层处理完毕
level++;
last=rear;
}
if(level>k)
return leaveCount;
}
}
2)方法二:
使用题目21中的方法,求出每一个节点的层次,然后遍历队列,统计层次为 i 的叶子节点的个数即可。
--------
(23)输出从每一个叶子节点到根节点的逆路径:(即从根节点到叶子节点的路径)
1)分析:采用层次遍历的方法。队列为非环形顺序队列。
队列中的每个元素为节点指针,以及当前节点的父节点的索引的结构体。
2)代码分析:
void allPath(BTNode* b){
struct snode{
BTNode* node;
int parentIndex;//保存父节点的索引
} Qu[Maxsize];
int front,rear;
int tempParentIndex;
front=rear=-1;//初始化
rear++;//根节点入队
Qu[rear].node=b;
Qu[rear].parentIndex=-1;
BTNode* pnode;
while(front<rear){
front++;//出队
pnode=Qu[front].node;
if(pnode->lchild==NULL && pnode->rchild==NULL){
tempParentIndex=front;
while(Qu[tempParentIndex].parentIndex!=-1){
//tempParentIndex不是根节点则循环进行。
visit(Qu[tempParentIndex].node->data);
tempParentIndex=Qu[tempParentIndex].parentIndex;
}
visit(Qu[tempParentIndex].node->data);
}
if(pnode->lchild){//左孩子进队
rear++;
Qu[rear].node=pnode->lchild;
Qu[rear].parentIndex=front;//因为pnode对应的编号为front
}
if(pnode->rchild){//右孩子进队
rear++;
Qu[rear].node=pnode->rchild;
Qu[rear].parentIndex=front;//因为pnode对应的编号为front
}
}
}
或者:
1)采用path数组存放路径,pathlen为数组长度。
2)递归模型:
当b为叶子节点时; f(b,path,pathlen)中输出path的值;
其他情况; f(b,path,pathlen)中,
将b->data的值放入到path中,pathlen++;
f(b->lchild,path,pathlen);
f(b->rchild,path,pathlen);
3)代码实现:
/*path存放的都是叶子节点的祖先节点,并没有存放叶子节点*/
void allPath(BTNode* b , DataType []path ,int pathlen){
if(b){
if(b->lchild==NULL && b->rchild==NULL){
visit(b->data);//访问叶子节点。
for(int i=pathlen-1;i>=0;i--){
visit(path[i]);
}
}
else{
path[pathlen]=b->data;//当前节点放入路径中
pathlen++;
allPath(b->lchild,path,pathlen);//扫描左子树
allPath(b->rchild,path,pathlen);//扫描右子树
pathlen--; //恢复环境?????
}
}
}
---------
(24)输出二叉树中最长的一条路径;
1)分析:
最长的路径是从根节点到某一个叶子节点的路径;
path保存当前的路径,pathlen为路径的长度;
longpath保存最长的路径,longpathlen保存其长度。
当b==NULL,表示当前的扫描已经结束,将pathlen与longpathlen比较。
2)代码实现:
void longPath(BTNode* b , DataType [] path ,int pathlen ,DataType longPath, int longpathlen){
int i;
if(b==NULL){ //当前路径更长,保存在longpathlen中。
if(pathlen>longpathlen){
for(i=pathlen-1;i>=0;i--)
longpath[i]=path[i];
longpathlen=pathlen;
}
}
else {
path[pathlen]=b->data;
pathlen++;
longPath(b->lchild,path,pathlen,longpath,longpathlen);
longPath(b->rchild,path,pathlen,longpath,longpathlen);
pathlen--;
}
}
--------
(25)输出值为x的节点的所有祖先;
1)分析:
f(b,x)表示b节点是否是值为x的节点的祖先,若是则f(b,x)=1;
否则,f(b,x)=0;
2)递归模型:
当b=NULL; f(b,x)=0;
当b->data=x; f(b,x)=1;
当f(b->lchild,x)=1或f(b->rchild,x)=1; f(b,x)=1,并输出b->data;
3)代码实现:
int allAncestor(BTNode* b ,DataType x){
if(b==NULL) return 0;
else if(b->data==x) return 1;
if( allAncestor(b->lchild,x) || allAncestor(b->rchild,x) ){
visit(b->data);
return 1;
}
}
注:基于后序。
------
(26)输出从根节点到节点值为x的节点的路径;
1)方法一:同上
2)方法二:采用非递归后序遍历,当后序遍历到值为x的节点时,此时栈中的所有节点均是该节点的祖先节点,这些祖先节点变构成了一条从跟节点到该节点的路径。
void ancestorPath(BTNode * b , DataType x){
BTNode* stack[Maxsize];
int top=-1;
int flag;
BTNode* pnode=b;
BTNode* preVisited;
if(b){
do{
while(pnode){//将pnode的所有左节点进栈。
top++;
stack[top]=pnode;
pnode=pnode->lchild;
}
preVisited=NULL;//preVisited为栈顶节点的前一个已经访问过的节点。
flag=1;
while(top!=-1 && flag){
pnode=stack[top];//取栈顶元素
if(pnode->rchild==preVisited){
//右孩子不存在或已经访问过,则访问之
if(pnode->data==x){
//找到值为x的节点,则栈中都是祖先节点。
for(int i=0;i<=top;i++)
visit(stack[i]->data);
}
top--;
preVisited=pnode;
}
else{
pnode=pnode->rchild;
flag=0;//flag=0表示栈顶节点处理完毕。
}
}
}while(top!=-1);//栈非空
}
}
------
(27)求节点值为x和节点值为y的两个节点的最近共同祖先;
或求包含x和y两个节点的最小子树。
1)分析:
采用非递归后序遍历,当找到节点值为x的节点时,将栈中的所有的节点值放在ancestor_x数组中,(找到x时,栈中的 节点均是x的祖先);
当找到值为y的节点时,将栈中的所有的节点值放在ancesotr_y数组中;
当两个节点均找到后,通过比较找出它们的公共祖先。
2)代码实现:
int commonAncestor(BTNode* b ,DataType x, DataType y){
DataType ancestor_x[Maxsize],ancestor_y[Maxsize];
BTNode* stack[Maxsize];
BTNode* pnode=b;
BTNode* preVisited;//当前栈顶节点的前一个被访问的 节点
int flag,top=-1;
bool findx=false, findy=false;
if(NULL!=b){
do{
while(pnode!=NULL){//将pnode的所有左孩子放到栈中。
top++;
stack[top]=pnode;
pnode=pnode->lchild;
}
flag=1;
preVisited=NULL;
while(top!=-1 && 1==flag){
pnode=stack[top];//访问栈头
if(pnode->rchild==preVisited){
//右孩子不存在或右孩子已经访问过
if(pnode->data==x){
for(int i=0;i<=top;i++)
ancestor_x[i]=stack[i]->data;
findx=true;
}
else if(pnode->data==y){
for(int i=0;i<=top;i++)
ancestor_y[i]=stack[i]->data;
findy=true;
}
if(finx && findy){
int i=0;
while(ancestor_x[i]==ancestor_y[i])
i++;
visit(ancestor_x[i-1]);//公共最近祖先
return 1;
}
top--;
preVisited=pnode;
}
else{
pnode=pnode->rchild;
flag=0;//退出当前循环。
}
}
}while(top!=-1);
}
return 0;//没有找到共同祖先。
}
--------
(28)算术表达式以中序二叉树形式存储,求表达式的值;
1)分析:根节点用于存储运算符,若能分别求出左子树和右子树的表达式的值,最后就可以计算出结果。
2)代码实现:
float expressionValue(BTNode* b){
float lvalue,rvalue,finalvalue;
if(b!=NULL){
if(b->data!='+' && b->data!='-' && b->data!='*' && b->data!='/')
return (b->data-'0');
else {
lvalue=expressionValue(b->lchild);
rvalue=expressionValue(b->rchild);
switch(b->data){
case '+': finalvalue=lvalue+rvalue;break;
case '-': finalvalue=lvalue1rvalue;break;
case '*': finalvalue=lvalue*rvalue;break;
case '/': if(rvalue!=0)
finalvalue=lvalue/rvalue;
else exit(0);break;
}
}
}
return finalvalue;
}
--------
(29)中序输出上题中的表达式,并加上括号;
1)分析:
中序遍历二叉树,当根节点和左孩子(或右孩子)都是运算符时,比较他们的优先级,当根节点的优先级高时,需要加上括号。
2)代码实现:
/*
比较运算符的优先级
op1>op2 返回1;
op1=op2 返回0;
op1<op2 返回-1;
*/
int precedency(char op1, char op2){
if(op1!='+' && op1!='-' && op1!='*' && op1!='/')
return -1;//数据的优先级是相同的
if(op2!='+' && op2!='-' && op2!='*' && op2!='/')
return -1;
switch(op1){
case '+':
case '-': if('+'== op2 || '-'==op2)
return 0;
else return -1; ;break;
case '*':
case '/': if('+'== op2 || '-'==op2)
return 1;
else return 0; ;break;
}
}
void inorderExpre(BTNode* b){
int precede;//记录优先级
if(b!=NULL){
if(b->lchild){
precede=precedency(b->data,b->lchild->data);
if(1==precede)
printf("(");//给左孩子加括号。
inorderExpre(b->lchild);
if(1==precede)
printf(")");
}
visit(b->data);//输出根节点。
if(b->rchild){
precede=precedency(b->data,b->rchild->data);
if(1==precede)
printf("(");//给左孩子加括号。
inorderExpre(b->rchild);
if(1==precede)
printf(")");
}
}
}
--------
(30)将上题中的表达式按照后序百丽得到后序表达式,然后求值。
1)分析:
1.先后序遍历得到后缀表达式;
2.后缀表达式求值:从左到右扫描后缀表达式,遇到数字就进栈,遇到操作符就出栈两个操作数,进行相应的运算,然后把结果入栈,知道后缀表达式结束,栈中只有一个元素,就是最终的结果。
2)代码实现:
char postExp[Maxsize];
int n=0;
void postExpre(BTNode* b){
if(b){
postExpre(b->lchild);
postExpre(b->rchild);
postExp[n++]=b->data;
}
}
float computeValue(){
float stack[Maxsize];
int top=-1;
float oper1,oper2,oper;//操作数1, 操作数2,运算结果。
char ch;//ch保存后缀表达式的一个字符。
int i=0;
while(i<n){//扫描后缀表达式
ch=postExp[i++];
switch(ch){
case '+':
oper1=stack[top];top--;//操作数1出栈
oper2=stack[top];top--;//操作数2出栈
oper=oper1+oper2;
top++;//操作结果进栈
stack[top]=oper;
break;
case '-':
oper1=stack[top];top--;//操作数1出栈
oper2=stack[top];top--;//操作数2出栈
oper=oper2-oper1;
top++;//操作结果进栈
stack[top]=oper;
break;
case '*':
oper1=stack[top];top--;//操作数1出栈
oper2=stack[top];top--;//操作数2出栈
oper=oper2*oper1;
top++;//操作结果进栈
stack[top]=oper;
break;
case '/':
oper1=stack[top];top--;//操作数1出栈
oper2=stack[top];top--;//操作数2出栈
if (0==oper1)
exit(0);
oper=oper2/oper1;
top++;//操作结果进栈
stack[top]=oper;
break;
default: //操作数进栈
top++;
stack[top]=ch-'0';break;
}
}
return stack[0];
}
--------
(31)给二叉树的存储结构中的每一个节点加上一个parent指针,设计一个算法,给这个指针赋值,并输出所有节点到根节点的 逆路径。
1)分析:采用先序递归。
/*
根节点的父节点为NULL,所以调动时,传递给b为根节点,p的值为NULL
*/
void TriBTree(BTNode* b , BTNode* p){
if(b){
b->parent=p;
TriBTree(b->lchild,b);
TriBTree(b->rchild,b);
}
}
void allPath(BTNode* b){
BTNode *pnode;
if(b){
pnode=b;
printf("当前节点%c到根节点 的逆路径\n",pnode->data);
while(pnode){//输出当前节点pnode到根节点的路径
visit(pnode->data);
pnode=pnode->parent;
}
allPath(b->lchild);//扫描左子树
allPath(b->rchild);//扫描右子树
}
}
------
(32)判读一棵树是否为完全二叉树;
1)分析:
采用层次遍历的方法,从上到下,从左到右的层次遍历,需要满足下面两个条件:
1.某个节点没有左孩子,一定无右孩子。
2.某个节点缺少右孩子,则按上到下,从左到右的顺序的下一个节点一定无孩子。
2)代码实现:
/*
complete表示二叉树为完全二叉树,初值为1 ,一定不满足上面两个条件则complete=0;
doubleSon表示所有节点均有两个孩子,初值为 1,一旦某个节点只有一个孩子,则置0.
*/
int completeBTree(BTNode* b){
BTNode * Queue[Maxsize];
BTNode * pnode;
int complete=1,doubleSon=1;
int front=rear=0;
if(b!=NULL){
rear++;//根节点入队
Queue[rear]=b;
while(front!=rear){
front++;
pnode=Queue[front];
if(NULL==pnode->lchild){//pnode左孩子为空
doubleSon=0;
if(pnode->rchild!=NULL)
complete=0;
}
else{
if(1==doubleSon){//pnode的前面没有缺少孩子的节点。
rear++;//左孩子进队
Queue[rear]=pnode->lchild;
if(NULL==pnode->rchild)//右孩子为空
doubleSon=0;
else{//右孩子非空
rear++;//右孩子进队
Queue[rear]=pnode->rchild;
}
}
else //doubleSon=0;说明已经有节点缺少孩子了。
complete=0;
//当前的pnode节点为缺少孩子节点的后继节点,但pnode有左孩子。违反2
}
}
return complete;
}
else return 1;//默认空二叉树为完全二叉树
}
--------
(33)已知二叉树存储在数组A[n]中,其中每个节点存储在数组的一个元素中,每个元素有3个字段,分别为data,ltag,rtag,其中data存储节点的值,ltag,rtag分别为左右孩子的下标,当没有左右孩子时,对应的下标为-1,根据如此的数组构造一个二叉链树。
1)举例如下图:
2)代码实现:
/*
数组中的元素时按照先序遍历存储的。
STree为结构体类型,包含三个字段,data,ltag,rtag。
i表示下标。
*/
BTNode* createTree(STree * A, int i){
BTNode* b;
b=(BTNode*) malloc(sizeof(BTNode));//根据A[i]中的data创建一个新节点。
b->data=A[i].data;
if(A[i].ltag!=-1){
b->lchild=createTree(A,A[i].ltag);
}
else b->lchild=NULL;
if(A[i].rtag!=-1){
b->rchild=createTree(A,A[i].rtag)
}
else b->rchild=NULL;
retrun b;
}
------
(34)已知二叉树按照先序次序依次存储在数组A[n]中,每个节点存储在数组的一个元素中,每个元素有三个字段,分别为data,ltag,rtag,data表示节点的值,ltag,rtag表示是否有左右孩子,若有左右孩子,则对应的 标志为1,否则为0。
构造一个链式二叉树。
1)举例如图所示:
2)分析:
设置一个全局变量i(初始值为0)扫描数组A,采用先序遍历方式遍历数组A,先建立根节点b,如果左孩子,则下标i+1,则递归构造左子树;若有右孩子,则数组下标i+1,递归构造右子树;
注意:i为全局变量,例如i=0时,A[0].ltag=1,需要构造左子树,当左子树构造完毕i=5,此时无法判断A[0].rtag,
所以将rtag,ltag作为参数。
3)代码实现:
/*
STree是结构体数据类型,包含data,ltag,rtag三个字段。
i为全局变量,
所以根据数组A,以及下标i,来创建一个节点,ltag,rtag来判断是否有左右子树。
*/
//创建一个节点需要有数组名A,元素下标i,以及该节点的ltag,rtag来判断是否有左右孩子。
int i=0;
BTNode* createTree(STree A[], int ltag ,int rtag){
BTNode* b;
b=(BTNode*) malloc(sizeof(BTNode));
b->data=A[i].data;
i++;
if(1==ltag) //左子树递归构造左子树
b->lchild=createTree(A,A[i].ltag, A[i].rtag);
else
b->lchild=NULL;
if(1==rtag)
b->rchild=createTree(A,A[i].ltag,A[i].rtag);
else b->rchild=NULL;
return b;
}
-------
(35)已知一棵完全二叉树存放在一个数组T[length]中,每个元素为各个节点的值。建立二叉树链式表示。
1)分析:
数组是从0号位置开始,所以节点i的左孩子下标为2*i+1,右孩子为2*i+2;
2)注意:
使用了引用型参数ptr,目的是把新建立的根节点带回上一层(即实参一层)。
如:如果原来是空树,函数将root(实参,=NULL)通过ptr(作为root的别名)传入,函数建立新节点,该节点的地址直接放入root,成为该树的根节点。如果原来是非空树,ptr成为上一层ptr的lchild或者rchild的别名,新节点的地址将直接传入上一层ptr的lchild或rchild中,实现自动链接。
3)代码实现:
/*
根据T[i]来创建一个新节点ptr;
*/
void createTree(DataType T[], int length ,int i , BTNode* & ptr){
if(i>=length) ptr=NULL;
else {
ptr=(BTNode*) malloc(sizeof(BTNode));//建立根节点。
ptr->data=T[i];
createTree(T,length , 2*i+1,ptr->lchild);//递归建立左子树
createTree(T,length , 2*i+2,ptr->rchild);//递归建立右子树
}
}
注:初次调用时,i=0;ptr用root(=NULL)作为实参代入。
------
(36)将一个用二叉链表示的 完全二叉树用二叉树的顺序表示;
/*
将t节点的data保存在A[i]中,
则t->lchild保存在A[2*i+1];
t->rchild保存在A[2*i+2]中;
*/
void linkList_to_Array(BTNode* t ,DataType []A, int length ,int i){
if(NULL==t) return ;
if(i>=n) {
printf("内存不足");
exit(0);
}
A[i]=t;
linkList_to_Array(t->lchild, A,length ,2*i+1);
linkList_to_Array(t->lchild, A,length ,2*i+1);
}
注:基于先序
-------
(37)求出二叉树t中指定节点p的父节点;
注:基于先序;
BTNode* getParent(BTNode* t, BTNode* p){
BTNode* parent;
if(NULL==t) return NULL;
if(t->lchild==p || t->rchild == p)
return t;
parent=getParent(t->lchild,p);
if(parent!=NULL) return parent;
else return getParent(t->rchild,p);
}
---
(38)求二叉树中各节点的元素的最大值;
注:最大值使用引用型参数
void maxValue(BTNode* t , DataType & max ){
if(t!=NULL){
if(t->data>max) max=t->data;
maxValue(t->lchild,max);
maxValue(t->rchild,max);
}
}
------
(39)给定一棵二叉树的先序序列为pre[s1,t1]和中序序列in[s2,t2],构造链式二叉树。
1)分析:
若s1<t1,则以pre[s1]建立二叉树的 根节点,然后在in[s2,t2]中搜索,寻找in[i]==pre[s1]的位置i,从而把中序序列分为in[s2,i-1]和in[i+1,t2];然后再以pre[s1+1,s1+(i-s2)] 和 in[s2,i-1]来构造左子树,以pre[s1+i-s2+1,t1]和in[i+1,t2]
递归构造跟的右子树。
2)代码实现:
void createBTree(BTNode* & t ,DataType pre[], DataType in[], int s1,int t1,int s2, int t2){
int i;
if(s1<=t1){
t=(BTNode*) malloc(sizeof(BTNode));//创建根节点。
t->data=pre[s1];
t->lchild=t->rchild=NULL;
for(i=s2;i<=t2;i++)
if(in[i]==pre[s1]) break;//在中序in[]中找到根节点的位置i。
createBTree(t->lchild,pre ,in,s1+1,s1+i-s2,s2,i-1);
//(s1+i-s2)-(s1+1)==(i-1)-s2;
createBTree(t->rchild,pre ,in,s1+i-s2+1,t1,i+1,t2);
//t1-(s1+i-s2+1)==t2-(i+1);
}
}
-------
(40)给定二叉树的后序遍历post[s1,t1]和中序 序列in[s2,t2],构造二叉树;
1)分析:
与上题类似,只不过
1.后序序列中二叉树根节点的位置在后序序列的最后的t1位置;
2.递归构造二叉树是先后后左;
3.子序列的边界不同;
2)代码实现:
void createBTree(BTNode* & t, DataType post[],DataType in[], int s1, int t1, int s2 , int t2){
if(s1<=t1){
t=(BTNode*) malloc(sizeof(BTNode));
t->data=post[t1];
t->lchild=t->rchild=NULL;
int i;
for(i=s2;i<=t2;i++)//在中序in[]中找到根节点的下标i
if(in[i]==post[t1]) break;
createBTree(t->rchild,post,in,s1+i-s2,t1-1,i+1,t2);//创建右子树
createBTree(t->lchild,post,in,s1,s1+i-s2-1,s2,i-1);//创建左子树
}
}
-------
(41)求二叉树的前序遍历中的第k个节点;
1)分析:
在前序遍历中加入引用型参数计算器count;
2)代码实现:
BTNode * pre_search(BTNode* t ,int & count, int k){
if(t!=NULL){
count++;
if(count==k) return t;
BTNode* pnode;
pnode=pre_search(t->lchild,count,k);
if(pnode) return pnode;
else return pre_search(t->rchild,count,k);
}
return NULL;
}
注:调用时count的初始值为0;
------
(42)利用二叉树的前序遍历求任意两个指定节点i和j的路径以及长度;
1)范例:
设I和J是树的两个节点,设从根节点A到节点I的路径是ABCDEI,从根节点到节点J的路径是ABPQRJ,则从节点I到节点J的路径是IEDCBPQRJ,长度为8.
2)思路:可以求出两个节点到根节点的路径,然后由此得到节点 i 和节点 j 的最近公共祖先节点 ancestor ,然后 节点 i 到节点 j 的最短路径 为 :节点 i 到 祖先节点 ancesotr , 然后祖先节点 ancesotr 到节点 j ;
---
(43)已知一棵满二叉树的前序序列,求后序序列;
1)分析:
f(pre,s1,t1, post,s2,t2);满二叉树中任何一个节点的 左右子树均有相同数目的节点。
2)递归模型:
当 s1>t1时, f(pre,s1,t1,post,s2,t2)中不做任何事;
当 s1<=t1时, f(pre,s1,t1,post,s2,t2)中,
post[t2]=pre[s1];
取中间位置half=(t1-s1)/2;
将pre[s1+1,s1+half]左子树转化为post[s2,s2+half-1];
即:f(pre,s1+1,s1+half,post ,s2,s2+half-1);
将pre[s1+half+1,t1]右子树转化为post[s2+half,t2-1];
即:f(pre,s1+half+1,t1,post, s2+half,t2-1);
3)代码实现:
void pre_to_post(DataType pre[],int s1,int t1, DataType post[] ,int s2,int t2){
int half;
if(s1<=t1){
post[t2]=pre[s1];
half=(t1-s1)/2;
pre_to_post(pre,s1+1,s1+half,post,s2,s2+half-1);//转换左子树
pre_to_post(pre,s2+half+1,t1,post,s2+half,t2-1);//转换右子树
}
}