二叉树结构体(带权重,父节点)
//二叉树的定义
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:在中序线索二叉树中查找指定节点在后序里面的前驱
思路:分为三种情况:
- 是否有右子树,有则直接是后续遍历的前驱
- 无右子树是否有左子树,有则直接是后续遍历的前驱
- 既无右子树也无左子树,基于中序线索遍历的特性向上查找一个含有左子树的父节点,这个节点的左孩子即为后续遍历的前驱
代码:
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;
}
}
以上代码仅供参考,如有错误恳请指正,谢谢!