树和二叉树的知识点总结

树和二叉树的定义

1、树(Tree):是n(n>=0)个结点的有限集。

  • 若n = 0,称为空树;
  • 若n > 0,则它满足如下两个条件:
    1)有且仅有一个特定的称为的结点;
    2)其余结点可分为m(m>=0)个互不相交的有限集T1,T2,T3,…,Tm,其中每一个集合本身又是一棵树,并称为根的子树。

2、树的基本术语
在这里插入图片描述

	- 结点:树中的一个独立单元。
	- 结点的度:结点拥有的子树数成为结点的度。
	- 树的度:树的度是树内各结点度的最大值。
	- 叶子:度为0的结点称为叶子或中端结点。
	- 非终端结点:度不为0的结点称为非终端结点或分支结点。
	- 双亲和孩子:结点的子树的根称为该结点的孩子,相应地,该结点称为孩子的双亲。,。
	- 兄弟:同一个双亲的孩子之间互称兄弟。
	- 祖先:从根到该结点所经分支上的的所有结点。
	- 子孙:以某结点为根的子树中的任一结点都称为该结点的子孙。
	- 层次:结点的层次从根开始定义起。
	- 堂兄弟:双亲在同一层的结点互为堂兄弟但不为同一个双亲。
	- 树的深度:树种结点的最大层次称为树的深度或高度。
	- 有序树和无序树:如果将树中结点的各子树看成从左只有是有次序的(即不能互换),则称该树为有序树,否则称为无序树。(在有序树中最左边的子树的根称为第一个孩子,最右边的称为最后一个孩子)
	- 森林:是m(m>=0)棵互不相交的树的集合。

3、二叉树的定义

定义-> 二叉树是n(n>=0)个结点所构成的集合,它或为空树(n=0);或为非空树。
对于非空二叉树:
	 - 有且仅有一个称之为根的结点;
	 - 除根结点以外的其余结点分为两个互不相交的子集T1和T2,分别称为T的左子树和右子树,且T1和T2本身又都是二叉树。

4、二叉树和树的区别

	-二叉树的每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点)。
	- 二叉树的子树有左右之分,其次序不能任意颠倒。

5、特殊二叉树

(1)满二叉树
 - 满二叉树:一颗深度为k且有2^k-1个结点的二叉树称为满二叉树

在这里插入图片描述

 - 特点
 	1)每一层上的结点数都是最大结点数(即每层都满)。
 	2)叶子节点全部在最底层。
 	3)满二叉树在同样深度的二叉树中结点个数最多
 	4)满二叉树在同样深度的二叉树中叶子结点个数最多
 -对满二叉树进行编号
 	1)规则:从根结点开始,自上而下,自左而右。
 	2)每一结点位置都有元素。
 (2)完全二叉树:
 -定义:深度为k的具有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应时,称之为完全二叉树。

对上图满二叉树来说
以下情况为完全二叉树
在这里插入图片描述
在这里插入图片描述
以下情况为非完全二叉树
在这里插入图片描述

-特点:
 	1)叶子只可能分布在层次最大的两层上。
 	2)对任一结点,如果其右子树的最大层次为i,则其左子树的最大层次必为i或i+1。
 -关系
 	满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树

二叉树的性质和存储结构

1、二叉树的性质

 - 在二叉树的第i层上至多有1^(i-1)个结点(i>=1)。
 - 深度为k的二叉树至多有2^k-1个结点(k>=1).
 - 对任何一棵二叉树T,如果叶子数为n0,度为2的结点数为n2,则n0=n2+1
 - 具有n个结点的完全二叉树的深度为⎿log2n⏌+1
 - 如果对一棵树有n个结点的完全二叉树(深度为⎿log2n⏌+1)的结点按层序编号(从第一层)
 到第⎿log2n⏌+1层,每层从左到右),则对任意结点i(1<=i<=n),有:
 1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点⎿i/2⏌。
 2)如果2i>n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i。
 3)如果2i+1>n,则结点i无右孩子;否则,其右孩子是结点2i+1。

2、二叉树的存储结构:
在这里插入图片描述

二叉树的顺序存储
	->按满二叉树的结点层次编号,依次存放二叉树中的数据元素。
//仅供理解
#define MAXTSIZE 100
Typedef TElemType SqBiTRee[MAXTSIZE]
SqBiTree bt;

假设有
在这里插入图片描述
这样一棵二叉树,那么它的存储形式为:
在这里插入图片描述
在这里插入图片描述

上图(root)所示。

二叉树的顺序存储区缺点:
最坏情况:深度为k的且只有k个结点的单支树需要长度为2^k -1的一维数组。
特点:
结点间关系蕴含在其存储位置中
浪费空间,适于存满二叉树和完全二叉树。

3、二叉树的链式存储结构:

typedef struct BiNode{
	TElemType data;
	struct BiNode *lchild,*rchild;
}BiNode,*BiTree;

如图:
对root图来说,其二叉链表形式如下:
在这里插入图片描述

–>在n个结点的二叉链表中,有n+1个空指针域
空指针数目 = 2n(总链域) - (n-1)(存放指针的链域) = n+1

三叉链表(哈夫曼树)

typedef struct TriTNode{
	TelemType data;
	struct TriTNode *lchild,*parent,*rchild;
}TriTNode,*TriTree;

对三叉链表来说,只是在二叉链表的基础上多了一个指向双亲结点的指针。
如图:
在这里插入图片描述

遍历二叉树

1、遍历的定义、目的及用途

定义:顺着某一条搜索路径巡防二叉树中的结点,使得每个结点均被访问一次,
而且仅被访问一次(又称周游)。
目的:得到树中所有结点的一个线性排列
用途:它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算
的基础和核心

2、二叉树的排列方式

L:遍历左子树
D:访问根结点
R:遍历右子树

方案:
DLR 、LDR 、 LRD 、DRL 、RDL 、RLD

其中常用的是:
DLR – 先序遍历
LDR – 中序遍历
LRD – 后序遍历
其中先中后都是指的根的位置

3、二叉树的遍历
(1)先序遍历:

若二叉树为空,则为空操作
否则
访问根节点
先序遍历左子树
先序遍历右子树
对root图来说,中序遍历之后的顺序为:

a - e - f - g - b - d - h - c

(2)中序遍历:

若二叉树为空,则为空操作
否则
中序遍历左子树
访问根节点
中序遍历右子树
对root图来说,后序遍历之后的顺序为:

g - f- e - a - h - d - b - c

(3)后序遍历:
若二叉树为空,则为空操作
否则
中序遍历左子树
中序遍历右子树
访问根节点
对root图来说,后序遍历之后的顺序为:

g - f - e - h - d - c - b - a

4、二叉树遍历的算法(运用递归):
1)先序遍历

Status PreOrderTraverse(BiTree T){
	if(T == NULL) return OK;//判断是否为空
	else{
		visit(T);//访问根节点-->cout<<T->data<<endl;
		PreOrderTraverse(T->lchild);//递归遍历左子树
		PreOrderTraverse(T->rchild);//递归遍历右子树
}

2)中序遍历

Status PreOrderTraverse(BiTree T){
	if(T == NULL) return OK;//判断是否为空
	else{
		PreOrderTraverse(T->lchild);//递归遍历左子树
		visit(T);//访问根节点-->cout<<T->data<<endl;
		PreOrderTraverse(T->rchild);//递归遍历右子树
}

3)后序遍历

Status PreOrderTraverse(BiTree T){
	if(T == NULL) return OK;//判断是否为空
	else{
		PreOrderTraverse(T->lchild);//递归遍历左子树
		PreOrderTraverse(T->rchild);//递归遍历右子树
		visit(T);//访问根节点-->cout<<T->data<<endl;
}

我们可以发现运用递归的思想,先序、中序、后序的遍历只是改变了访问根节点的位置。
下面我们不用递归来实现遍历二叉树。
5、遍历二叉树的非递归算法
中序遍历非递归算法

关键:在中序遍历过某结点的整个左子树后,如何找到该结点的根以及右子树。
基本思想:
①建立一个栈。
②根结点进栈,遍历左子树。
③根结点出栈,输出根结点,遍历右子树。
Status InOrderTraverse(BiTree T){
	BiTree p;
	InitStack(S);
	p =T;
	while(p || !StackEmpty(S)){
	if(p){ //p非空
		Push(S,p);  //根指针进栈
		p = p->lchild; //根指针进栈,遍历左子树
		}
	else{         //p为空
		Pop(S,q); //出栈
		cout<<q->data<<endl; //访问根节点
		p = q->rchild; //遍历右子树
		}
	}
	retuen OK;		

6、二叉树的层次遍历
算法思路:
1)将根结点入队;
2)队不空时循环:从队列中出列一个结点*p,访问它;
①:若它有左孩子结点,将左孩子结点进队;
②:若它有右孩子结点,将右孩子结点进队。

typedef struct{
	BTNode data[MaxSize]; //存放队中元素
	int front,rear;		//队头和队尾指针
}SqQueue;	//顺序循环队列类型
void LevelOrder(BTNode *b){
	BTNode *p;
	SqQueue *q;
	InitQueue(q);  //初始化队列
	enQueue(q,b);	//根结点指针进入队列
	while(!QueueEmpty(q)){	//队不为空,则循环
	deQueue(q,p);	//出队结点p
	cout<<p->data<<endl;	//访问结点p
	if(p->lchild != NULL) enQueue(q,p->lchild);	//有左孩子时将其进队
	if(p->rchild != NULL) enQueue(q,p->rchild);	//有有孩子时将其进队
	}
}	

7、二叉树的建立:
1)按先序遍历序列建立二叉树的二叉链表
例:已知先序序列为:
ABCDEGF
在这里插入图片描述

①从键盘输入二叉树的结点信息,建立二叉树的存储结构;
②在建立二叉树的过程中按照二叉树先序方式建立。
如要实现上图需插入字符的顺序为:
ABC##DE#G##F###

Status CreateBiTree(BiTree &T){
	scanf(&c);//-->cin>>c;
	if(c == '#') T = NULL; //递归结束,建空树
	else{	//递归建立二叉树
		if(!(T = BiTNode*)malloc(sizeof(BiTNode)))
		exit(OVERFLOW);//T = new BiTNode;
	T->data = c;//生成根结点
	CreateBiTree(T->lchild); //构造左子树
	CreateBiTree(T->rchild); //构造右子树
	}
	return OK;
}	

8、复制二叉树(利用先序遍历):
思想:
1)如果是空树,递归结束;
2)否则,申请新结点空间,复制根结点
递归复制左子树
递归复制右子树

Status Copy(BiTree T BiTree &NewT){
	if(T->NULL){ //如果是空树递归结束
		NewT = NULL;
		return 0;
	}else{
		NewT = new BiNode;
		NewT->data = T->data;	//复制根结点
		Copy(T->lchild,NewT->lchild); //递归创建左子树
		Copy(T->rchild,NewT->rchild); //递归创建右子树
	}
}

9、计算二叉树的深度:
思想:
1)如果是空树,则深度为0;
2)否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m和n的较大者加1。

Status Depth(BiTree T){
	if(T == NULL) return 0;
	else{
		m = Depth(T->lChild);
		n = Depth(T->rChild);
		if(m > n) return(n+1);
		else return(n+1);
		}
}

10、计算二叉树结点总数
1)如果是空树,则结点个数为0;
2)否则,结点个数为左子树的结点个数 + 右子树的结点个数再+1。

Status NodeCount(BiTree T){
	if(T == NULL) 
		return 0;
	else 
		return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;//加1是加上了根结点
}

11、计算叶子结点的总数:
思想:
1)如果是空树,则结点个数为0;
2)否则,为左子树的叶子结点个数 + 为右子树的叶子结点个数。

Status LeafCount(BiTree T){
	if(T == NULL) //如果是空树返回0
		return 0;
	if(T->lchild == NULL && T->rchild == NULL)
		return 1;//如果根结点是叶子结点返回1
	else
		return LeafCount(T->lchild) + LeafCount(T->rchild);//左子树叶子结点 + 右子树叶子结点
}
线索二叉树

1、 前提:利用二叉链表中的空指针域
–>如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前驱;
如果某结点的右孩子为空,则将空的右孩子指针域改为指向其后继
–这种改变指向的指针称为"线索"
加上了线索的二叉树称为线索二叉树。

对二叉树按某种遍历次序使其变为线索二叉树的过程叫线索化

线索二叉树的结点结构为

lchildltagdata rtagrchild

存储结构如下:

typedef struct BiThrNode{
	int data;
	int ltag,rtag;
	struct BiThrTree *lchild,rchild;
}BiThrNode,*BiThrTree;

一般情况下我们默认为:
ltag为 0是指向该结点的左孩子,为 1时指向该结点的前驱
rtag为 0是指向该结点的右孩子,为 1时指向该结点的后继

我们仍然对root图来说
按中序遍历的话,结点的顺序为:

g - f - e - a - h - d - b - c

则线索二叉树如图:
在这里插入图片描述

如果我们遍历二叉树的首个结点的左子树为空的话,则没有前驱,同理,最后一个结点的右子树为空的话没有后继,所以我们来增设一个头结点
作用:
操作方便,避免指针悬空

如下图:
在这里插入图片描述

  • 17
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值