二叉树的常见考法1

二叉树结构体(带权重,父节点)

//二叉树的定义
typedef struct BNode{
	int data;
	struct BNode *lchild,*rchild,*parent;
	//二叉树的权重 
	int weight;
}BNode,*BiTree; 

创建二叉树

BiTree createBiTree(){
	int data;
	scanf("%d",&data);
	if(data!=-1){
		BiTree t  = (BiTree)malloc(sizeof(BNode));
		t->data = data;
		t->lchild = createBiTree();
		//父节点建立 
		if(t->lchild!=NULL){
			t->lchild->parent = t;
		}
		t->rchild = createBiTree();
		if(t->rchild!=NULL){
			t->rchild->parent = t;
		}
		//权重节点建立 
		if(!t->lchild && !t->rchild){
			scanf("%d",&data);
			t->weight = data;
		}
		return t;
	}else{
		return NULL;
	}
}

 

先温习一下遍历方式

三大遍历 递归版

//前序递归法 
void preOrder(BiTree t){
	if(t==NULL)return;
	visit(t);
	preOrder(t->lchild);
	preOrder(t->rchild);
}
//中序递归法
void inOrder(BiTree t){
	if(t==NULL)return;
	inOrder(t->lchild);
	visit(t);
	inOrder(t->rchild);
}
//后续递归法
void postOrder(BiTree t){
	if(t==NULL)return;
	postOrder(t->lchild);
	postOrder(t->rchild);
	visit(t);
} 

三大遍历 非递归版

//前序非递归
void upreOrder(BiTree t){
	BiTree Stack[MaxSize],p=t;
	int top = -1;
	while(p || top!=-1){
		if(p){
			visit(p);
			Stack[++top] = p;
			p = p->lchild;
		}else{
			p = Stack[top--];
			p = p->rchild;
		}
	}
}
//中序非递归
void uinOrder(BiTree t){
	BiTree Stack[MaxSize],p=t;
	int top = -1;
	while(p || top!=-1){
		if(p){
			Stack[++top] = p;
			p = p->lchild;
		}else{
			p = Stack[top--];
			visit(p);
			p = p->rchild;
		}
	}
} 
//后续非递归 
void upostOrder(BiTree t){
	BiTree Stack[MaxSize],p=t;
	int top=-1,tag[MaxSize]={0};
	while(p || top!=-1){
		if(p){
			Stack[++top] = p;
			tag[top] = 1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				tag[top] = 2;
				p = p->rchild;
			}else{
				visit(p);
				top--;
				p = NULL;
			}
		}
	}
}

层序遍历(借助队列)

void leverOrder(BiTree t){
	BiTree Queue[MaxSize],p=t;
	int front=-1,rear=front;
	Queue[++rear] = p;
	while(front!=rear){
		p = Queue[++front];
		visit(p);
		if(p->lchild){
			Queue[++rear] = p->lchild;
		}
		if(p->rchild){
			Queue[++rear] = p->rchild;
		}
	}
}

考法1:给出二叉树自下而上,自右到左的层次遍历算法

思路:层序遍历,利用栈先进后出的特性,用栈存储内容,最后将栈内内容弹出即为题目要求。

代码:

void reversePrintBiTree(BiTree t){
	BiTree Stack[MaxSize],Queue[MaxSize],p=t;
	int top=-1,front=-1,rear=front;
	Queue[++top] = p;
	while(front!=rear){
		p = Queue[++front];
		Stack[++top] = p;
		if(p->lchild){
			Queue[++rear] = p->lchild;
		}
		if(p->rchild){
			Queue[++rear] = p->rchild;
		}
	}
	while(top!=-1){
		p = Stack[top--];
		visit(p);
	}
}

考法2:递归算法求解二叉树的高度

思路:先序遍历,只不过这回要加返回值,高度取左右子树中的最大值。

代码:

int getHeight(BiTree t){
        if(!t){
          return 0;
		}
		int left = getHeight(t->lchild);
		int right = getHeight(t->lchild);
		return left>right?left+1:right+1;
}

考法3:非递归方法求解树的高度

思路:后序非递归遍历,因后续遍历的特点是祖先全在栈内,这时只需要记录top指针的数值并比较即可

代码:

//3.非递归方法求解树的高度
int ugetHeight(BiTree t){
	BiTree Stack[MaxSize],p=t;
	int top = -1,max = -1,tag[MaxSize]={0};
	while(p || top!=-1){
		if(p){
			Stack[++top]=p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				tag[top]=2;
				p = p->rchild;
			}else{
				//叶子节点判断 
				if(!p->lchild && !p->rchild){
					//深度最大更新
					if(top+1>max)max = top + 1;
				}
				top--;
				p = NULL;
			}
		}
	}
	return max; 
}

考法4:求解二叉树的结点所在的层

思路:后序遍历,看栈内元素即可

代码:

int getLever(BiTree t,int data){
	BiTree Stack[MaxSize],p = t;
	int top = -1,tag[MaxSize]={0};
	while(p || top!=-1){
		if(p){
			Stack[++top] = p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				p = Stack[top];
				p = p->rchild;
				tag[top]=2;
			}else{
				if(data == p->data){
					return top+1;
				}
				top--;
				p = NULL;
			}
		}
	} 
}

以下题目基本上都利用遍历的递归思想,不再解释思路,读者请自行考虑

考法5:用递归方法求解树的结点所在的层

代码:

//5.用递归方法求解树的结点所在的层
void getLeverByR(BiTree t,int data,int &lever,int deep){
	if(t){
		//找到值了,保存值 
		if(t->data==data)lever = deep;
		//每次下降深度都得加一 
		getLeverByR(t->lchild,data,lever,deep+1);
		getLeverByR(t->rchild,data,lever,deep+1);
	}
}

考法6:使用递归方法求解树的高度(深度)

代码:

void getHeightByR(BiTree t,int curdeep,int &deep){
	if(t){
		//确认叶子节点 
		if(!t->lchild && !t->rchild){
			if(curdeep+1>deep){
			   deep = curdeep;
		  }
		}
		getHeightByR(t->lchild,curdeep + 1,deep);
	    getHeightByR(t->rchild,curdeep + 1,deep);
	}
	
}

考法7:用层序遍历求解树的高度

思路:层序遍历下每走完一层即树的高度加一即可!重要的是什么时候层序遍历什么时候算走完一层?可以先设置一个值deep=0,当front=deep时即走完一层并更新deep=rear

代码:

//7.使用层序遍历求解树的高度
int getHeightByLever(BiTree t){
	BiTree Queue[MaxSize],p = t;
	int front=-1,rear=front,height=0,deep=0;
	Queue[++rear]=p;
	while(rear!=front){
		p = Queue[++front];
		if(p->lchild){
			Queue[++rear] = p->lchild;
		}
		if(p->rchild){
			Queue[++rear] = p->rchild;
		}
		//检查当前层是否走完
		//当前层走完了,此时下一层所有元素应该已在队列中
		//这时保存队列最后一个位置,用于判断接下来一层什么时候遍历完成 
		if(deep == front){
			height++;
			deep = rear;
		}
	}
	return height;
}

考法8:求解树的宽度 (最大宽度)

思路:层序遍历取最大值

代码:

int getWidthByLever(BiTree t){
	BiTree Queue[MaxSize],p = t;
	int front=-1,rear=front,deep=0,maxwidth=-1;
	Queue[++rear]=p;
	while(rear!=front){
		p = Queue[++front];
		if(p->lchild){
			Queue[++rear] = p->lchild;
		}
		if(p->rchild){
			Queue[++rear] = p->rchild;
		}
		//检查当前层是否走完
		//当前层走完了,此时下一层所有元素应该已在队列中
		//这时保存队列最后一个位置,用于判断接下来一层什么时候遍历完成 
		if(deep == front){
        //队列长度更新
			if(rear - front >maxwidth){
				maxwidth = rear - front;
			}
			deep = rear;
		}
	}
	return maxwidth;
}

考法9:判断一棵树是否是一颗完全二叉树

思路:层序遍历,完全二叉树的特性:完全二叉树一层遍历完队列里应该无节点
如果有则证明有空节点,后面仍有多余节点,不是完全二叉树

代码:

bool isATBiTree(BiTree t){
	BiTree Queue[MaxSize],p = t;
	int front=-1,rear=front,deep=0;
	Queue[++rear]=p;
	while(rear!=front){
		p = Queue[++front];
		if(p){
			Queue[++rear] = p->lchild;
			Queue[++rear] = p->rchild;
		}else{
			//完全二叉树一层遍历完队列里应该无节点
			//如果有则证明有空节点,后面仍有多余节点,不是完全二叉树 
			while(front!=rear){
				p = Queue[++front];
				if(p)return false;
			}
		}
	}
	return true;
}

考法10:计算二叉树的所有双分支节点个数
求单分支节点
求叶子节点

代码: 

void getNodeCount(BiTree t,int &n){
	if(t){
		//叶子节点 
		if(!t->lchild && !t->rchild){
		    n++;
	    } 
	    //单分支 
//	    if(!t->lchild && t->rchild || t->lchild && !t->rchild){
//		    n++;
//	    } 
	    //双分支
//		 if(t->lchild && t->rchild){
//		    n++;
//	    } 
	getNodeCount(t->lchild,n);
	getNodeCount(t->rchild,n);
	}
} 

考法11:递归返回方法求解树的双节点数

代码:

//11.递归返回方法求解树的双节点数
int getDNodeByR(BiTree t){
	if(!t)return 0;
	//双分支节点加一 
	else if(t->lchild && t->rchild) return 1+getDNodeByR(t->lchild) + getDNodeByR(t->rchild);
	else return getDNodeByR(t->lchild) + getDNodeByR(t->rchild);
}

考法12:使用非递归方法交换左右子树

思路:层序遍历,对子树一个个交换即可

代码:

void swaplrchild(BiTree &t){
	BiTree Queue[MaxSize],p = t,temp;
	int front=-1,rear = front,deep=0;
	Queue[++rear] = p;
	while(front!=rear){
		p = Queue[++front];
		//开始交换
		temp = p->lchild;
		p->lchild = p->rchild;
		p->rchild = temp;
		//继续入队 
		if(p->lchild)Queue[++rear] = p->lchild;
		if(p->rchild)Queue[++rear] = p->rchild;	
	}
} 

考法13:使用递归方法交换左右子树

代码:

void swaplrchildByR(BiTree &t){
	if(t){
		//顺序不一致是否结果不一样呢? 
		BiTree temp = t->lchild;
	    t->lchild = t->rchild;
	    t->rchild = temp;
		if(t->lchild)swaplrchildByR(t->lchild);
		if(t->rchild)swaplrchildByR(t->rchild);
	}
}

考法14:先序遍历序列中第k个节点的值

代码:

void preOrderGetK(BiTree t,int k,int &deep,int &data){
	if(t){
		if(k==deep){
			data = t->data;
		}
		deep = deep + 1;
		preOrderGetK(t->lchild,k,deep,data);
		preOrderGetK(t->rchild,k,deep,data);
	} 
}

非递归实现:

int  upreOrderGetK(BiTree t,int k){
	BiTree Stack[MaxSize],p=t;
	int top = -1,count = 0;
	while(p || top!=-1){
		if(p){
			if(count == k)return p->data;
			count ++;
			Stack[++top] = p;
			p = p->lchild;
		}else{
			p = Stack[top--];
			p = p->rchild;
		}
	}
	return -1;
}

考法15:中序遍历序列中第k个节点的值

代码:

void inOrderGetK(BiTree t,int k,int &deep,int &data){
	if(t){
		inOrderGetK(t->lchild,k,deep+1,data);
		if(k==deep){
			data = t->data;
		}
		inOrderGetK(t->rchild,k,deep+1,data);
	} 
}

非递归实现:

int uinOrderGetK(BiTree t,int k){
	BiTree Stack[MaxSize],p=t;
	int top = -1,count = 0;
	while(p || top!=-1){
		if(p){
			Stack[++top] = p;
			p = p->lchild;
		}else{
			p = Stack[top--];
			if(count == k)return p->data;
			count ++;
			p = p->rchild;
		}
	}
	return -1;
}

考法16:后序遍历序列中第k个节点的值

代码:

void postOrderGetK(BiTree t,int k,int &deep,int &data){
	if(t){
		postOrderGetK(t->lchild,k,deep+1,data);
		postOrderGetK(t->rchild,k,deep+1,data);
		if(k==deep){
			data = t->data;
		}
	} 
}

非递归实现:

int upostOrderGetK(BiTree t,int k){
	BiTree Stack[MaxSize],p=t;
	int top = -1,count = 0,tag[MaxSize] = {0};
	while(p || top!=-1){
		if(p){
			Stack[++top] = p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				p = p->rchild;
				tag[top]=2;
			}else{
				if(count == k)return p->data;
			    count ++;
				top--;
				p = NULL;
			}
		}
	}
	return -1;
}

考法17:层次遍历序列中第k个节点的值

代码:

int leverOrderGetK(BiTree t,int k){
	BiTree Queue[MaxSize],p=t;
	int rear = -1,front = rear,count = 0;
	Queue[++rear] = p;
	while(rear!=front){
		p = Queue[++front];
		if(k == count)return p->data;
		count++;
		if(p->lchild)Queue[++rear] = p->lchild;
		if(p->rchild)Queue[++rear] = p->rchild;
	}
	//别忘记找不到返回-1 
	return -1;
}

考法18:除树中每个元素值为x的节点,并删除以它为根的子树

代码:

void deleteNodeByPost(BiTree &t){
	if(t){
		deleteNodeByPost(t->lchild);
		deleteNodeByPost(t->rchild);
		free(t);
	}
}
//层序遍历,寻找x节点 
void findXandDelete(BiTree &t,int data){
	BiTree Queue[MaxSize],p = t;
	int rear = -1,front = rear;
	while(rear!=front){
		p = Queue[++front];
//		if(p->data == x){
//		   deleteNodeByPost(t);
//		   p = NULL; 
//		} 
        if(p->lchild && p->lchild->data = x){
        	deleteNodeByPost(p->lchild);
        	p->lchild = NULL;
		}
		if(p->rchild && p->rchild->data = x){
        	deleteNodeByPost(p->rchild);
        	p->rchild = NULL;
		}
		if(p->lchild)Queue[++rear] = p->lchild;
		if(p->rchild)Queue[++rear] = p->rchild; 
	}
}

考法19:打印节点x的所有的祖先

思路:后序遍历时找到节点k此时祖先节点已全在栈内,打印栈内所有元素即可!

代码:

void printAllPNode(BiTree t,int x){
	BiTree Stack[MaxSize],p = t;
	int top = -1,tag[MaxSize] = {0};
	while(p || top!=-1){
		if(p){
			Stack[++top]=p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				tag[top]=2;
				p = p->rchild;
			}else{
				//检查节点值是否是x 
				if(x == p->data){
					int i = top;
					while(i!=-1){
						i--;
						printf("%d,",Stack[i]->data);
					}
				}
				top--;
				p = NULL;
			}
		}
	}
}

考法20:打印从根节点到某个节点的路径

思路:类似于上一题

代码:

void printRoutine(BiTree t,int x){
	BiTree Stack[MaxSize],p = t;
	int top=-1,tag[MaxSize]={0};
	while(p || top!=-1){
		if(p){
			Stack[++top]=p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				tag[top]=2;
				p = p->rchild;
			}else{
				if(p->data = x){
					for(int i=top;i!=-1;i--){
						printf("%d,",Stack[i]);
					}
				}
				top--;
				p = NULL;
			}
		}
	}
} 

以下代码基本采用的都是后序遍历的思路,读者请自行测试!不再阐述思路。 

考法21:求从根节点到某个节点的路径长度 (最大) 

思路:后序遍历,取栈的最大长度即可

代码:

int printRoutine(BiTree t,int x){
	BiTree Stack[MaxSize],p = t;
	int top=-1,tag[MaxSize]={0};
	while(p || top!=-1){
		if(p){
			Stack[++top]=p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				tag[top]=2;
				p = p->rchild;
			}else{
				if(p->data = x){
					return top+1;
				}
				top--;
				p = NULL;
			}
		}
	}
} 

考法22:根节点到某个节点最大路径长度

代码:

int printRoutine(BiTree t,int x){
	BiTree Stack[MaxSize],p = t;
	int top=-1,tag[MaxSize]={0},maxdeep=-1;
	while(p || top!=-1){
		if(p){
			Stack[++top]=p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				tag[top]=2;
				p = p->rchild;
			}else{
				if(p->data = x){
					if(top+1>maxdeep) maxdeep=top+1;
				}
				top--;
				p = NULL;
			}
		}
	}
	return maxdeep;
}

考法23:判断u是否为v的后代 v是为u的祖先

代码:

bool printRoutine(BiTree t,BiTree u,BiTree v){
	BiTree Stack[MaxSize],p = t;
	int top=-1,tag[MaxSize]={0},maxdeep=-1;
	while(p || top!=-1){
		if(p){
			Stack[++top]=p;
			tag[top]=1;
			p = p->lchild;
		}else{
			p = Stack[top];
			if(tag[top]==1){
				tag[top]=2;
				p = p->rchild;
			}else{
				if(p==v){
					for(int i=top;i!=-1;i--){
						if(Stack[i]==v)return true;
					}
				}
				top--;
				p = NULL;
			}
		}
	}
	return false;
}

考法24:判断两颗二叉树是否相似

思路:相似是指树的形状相似,两棵树同时遍历,如果有树的结点先没有即代表两棵树不相似

代码:

bool issimilar(BiTree u,BiTree v){
	if(!u || !v)return false;
	else if(!u && !v)return true;
	else return issimilar(u->lchild,v->lchild) && issimilar(u->rchild,v->rchild);
}

考法25:判断两颗二叉树是否相等 

思路:在上述题目的基础上增加对结点数据的判断即可

代码:

bool isequal(BiTree u,BiTree v){
	if(!u || !v)return false;
	else if(!u && !v)return true;
	else if(u->data!=v->data)return false;
	else return issimilar(u->lchild,v->lchild) && issimilar(u->rchild,v->rchild);
}

考法26:将叶子节点串成一个单链表,用叶子节点的rchild,作next

思路:对叶子节点判断形成一条单链表即可

代码:

void getLeafChain(BiTree &pre,BiTree &p){
	if(p){
		if(!p->lchild && !p->rchild){
			if(!pre){
				pre = p;
			}else{
				pre->rchild = p;
			    pre = p;
			}
		}
		getLeafChain(pre,p->lchild);
		getLeafChain(pre,p->rchild);
	}
}

考法27:带权结点路径计算

代码:

void getWPL(BiTree t,int &deep,int &wpl){
	if(t){
		if(!t->lchild && !t->rchild){
			wpl += (deep-1)*t->weight;
		}
		getWPL(t->lchild,deep+1,wpl);
		getWPL(t->rchild,deep+1,wpl);
	}
}

考法28:在中序线索二叉树中查找指定节点在后序里面的前驱

思路:分为三种情况:

  1. 是否有右子树,有则直接是后续遍历的前驱
  2. 无右子树是否有左子树,有则直接是后续遍历的前驱
  3. 既无右子树也无左子树,基于中序线索遍历的特性向上查找一个含有左子树的父节点,这个节点的左孩子即为后续遍历的前驱

代码:

ThreadTree inOrderFindPrePPost(ThreadTree p){
	//p有右孩子,返回右孩子 
	if(p->rtag == 0)return p->rchild;
	//p有左孩子,无右孩子,返回左孩子 
	else if(p->ltag == 0)return p->lchild;
	//p无左右孩子,则后续的前驱一定是他的父亲节点或爷爷节点
	//此时在中序线索序列里则为他的左指针域,且如果有线索则不停向上访问,
	//遍历,直到有一个祖先有左孩子,则这个左孩子一定是后序遍历p的前驱。
	else{
		while(p->lchild && p->ltag==1){
			p = p->lchild;
		} 
		if(p->ltag == 0)return p->lchild;
		else return NULL;
	}
}

以上代码仅供参考,如有错误恳请指正,谢谢!

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值