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

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

二叉树的概念

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]
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值