树是n(n>=0)个结点的有限集,n=0时空树
基本术语建议看书
重点学习二叉树(因为菜啊)
二叉树:要么是空集n=0,!!注意!!
要么由一个根结点和两棵互不相交的分别称作左右子树的二叉树组成(递归)
规定(背下来就好,我也不知道为啥,但是好像有点道理):二叉树不是不是不是不是不是树的特殊情况,他们是两个概念
二叉树性质 5个:
first:第i层上至多由2^(i-1)个结点(i>=1)//最少有1个结点,没有不就没了吗
second:深度为k的二叉树至多有2^(k) - 1个结点(k>=1) //最少k个
证明:用性质1,每层2^(i-1),相加,等比数列求和
third: any二叉树T,若其叶子 n0 个,度为2的结点数为 n2 ,n0 = n2 + 1。
证明:若总边数为B,结点数n,B = n - 1
//为啥呢?因为每一个结点都有自己的妈妈,但是root没妈
从另一个角度算,B = (2 * n2) + (1 * n1)
n-1 = 2*n2 + n1
n = 2*n2 + n1 + 1
又因为 n = n2 + n1 + n0
then,n0 = n2 + 1
这个性质用处不大
满二叉树知道是啥吗? 满呗,深度为k且又2^(k) - 1 个结点的二叉树
完全二叉树知道是啥吗? 编号和满二叉树一一对应,一点不一样都不行!
满二叉树中,从最后一个结点开始,连续去掉任意个结点,即使一棵完全二叉树
fourth:具有n个结点的 完全二叉树 的深度为【log2 (n)】+ 1
//【x】:称作x的底,表示不大于x的最大整数(下取整)
找不到那个符号啦,凑合看
证明:元素个数n,第k层,n的范围 (2^(k-1)-1 , 2^(k)-1] //性质2
或者 [ 2^(k-1) , 2^(k) ),取对数就得出来了
fifth: 有n个结点的 完全二叉树 的深度为【log2 (n)】+ 1 ,
则对任意节点i [1,n]
if i==1,结点i是根,无双亲;
if i>1, 双亲结点是【i/2】//记得这个符号的意思奥
if 2i>n,则结点为叶子,无左孩子;否走其左孩子是结点2i
if 2i+1>n,则结点i无有孩子;否则其右孩子为2i+1
//顺序存储时用到
二叉树顺序存储
实现:按满二叉树的结点层次编号,依次存放二叉树中的数据元素
#definr MAXTSIZE 100
typedef TElemType SqBiTree[MAXTSIZE]
SqBiTree bt;
适合满二叉树、完全二叉树
若出现右单支树(深度为k,结点数为k),浪费许多空间
********************
二叉链表
typedey struct BiNode{
TElemType data;
struct BiNode *lchild, *rchild
}BiNode,*BiTree;
在n个结点的二叉链表中,有n+1个空指针域
证明:必有2n个指针域,除了root以外,都有妈妈即n-1,所以空的就n+1
*******************
heavy之heavy
遍历
先序(根)遍历DLR
中序遍历LDR
后序遍历LRD
先序 ABELDHMIJ
中序 ELBAMHIDJ
后序 LEBMIHJDA
如果知道中序和先后之一可以构造出相应二叉树
每个结点路过三次,第一次经过时访问=先序,第二次=中序,第三次=后序
时间复杂度O(n)//每个结点只访问一次
空间复杂度O(n)//栈占用的最大辅助空间
int PreOrderTraverse(BiTree T){
//前序
if(T == NULL) return OK;
else{
cout<<T->data; //DLR
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
int InOrderTraverse(BiTree T){
//中序
if(T == NULL) return OK;
else{
InOrderTraverse(T->lchild); //LDR
cout<<T->data;
InOrderTraverse(T->rchild);
}
}
int PostOrderTraverse(BiTree T){
//后序
if(T == NULL) return OK;
else{
PostOrderTraverse(T->lchild);//LRD
PostOrderTraverse(T->rchild);
cout<<T->data;
}
}
非递归算法遍历
中序:在中序遍历某个过某结点的整个左子树后,如何找到该节点的root以及rchild
(1)建立一个栈
(2)根结点进栈,遍历左子树
(2)根结点出栈,输出根结点,遍历右子树
我tm巨讨厌伪代码
int InOrderTraverse(BiTree T) {
BiTree p;
InitStack(S);
p = T;
while(p||StackEmpty(S) ){
if(p){
Push(S,p);
p = p->lchild;
}else{
Pop(S,q);//q得自己定义
cout<<q->data;
p = q->rchild;
}
}
return OK;
}
层次遍历 利用Queue
void LevelOrder(BTNode *b){
BTNode *p;
SqQueue *qu;
InitQueue(qu);
Queue(qu);
enQueue(qu,b);//根节点指针进入队列
while(!QueueEmpty(qu)){
//若队列不为空,则循环
deQueue(qu,p);
cout<<p->data;
if(p->lchild != NULL) enQueue(qu,p->lchild)
//有左孩子时,将其入队
if(p->rchild != NULL) enQueue(qu,p->rchild)
//有右孩子时,将其入队
}
}
有啥应用呢?
1、按照先序遍历序列建立二叉树
已知先序:ABCDEGF
只有一个上面这种先序是无法唯一确定二叉树的,可以通过补充空结点(#)解决
ABC##DE#G##F###
int CreateBiTree(BiTree &T){
cin>>ch
if(ch == "#") T = NULL;
else{
if(!(T = (BiTNode *) malloc (sizeof(BiTNode)) ))
exit(OVERFLOW) //T = new BiTNode; C++我暂时还没试
T->data = ch; //生成根结点
CreateBiTree (T->lchild); //构造左子树
CreateBiTree (T->rchild); //构造右子树
}
return OK;
}
2、Copy二叉树
if是空树,递归结束
else,申请新节点空间,复制根结点
递归copy左子树
递归copy左子树
int Copy(BiTree T, BiTree &NewT){
if(T == NULL){
//如果是空树返回0
NewT = NULL;
return 0;
}else{
NewT = new BiTNode;
NewT->data = T->data; //先将根结点复制过去
Copy(T->lchild,NewT->lchild);
Copy(T->lchild,NewT->lchild);
}
3、深度计算
如果是空树,深度为0
否则递归计算左子树深度m,递归计算右子树深度n,二叉树深度为m与n的较大者+1
int Depth(BiTree T){
if(T == NULL){
//如果是空树返回0
return 0;
}else{
m = Depth(T->lchild);
n = Depth(T->rchild);
if(m>n) return (m+1);
else return (n+1);
}
}
4、求结点总数
int NodeCount(BiTree T){
if(T == NULL){
//如果是空树返回0
return 0;
}else return NodeCount(T->lchild) + NodeCount(T->rchild)+1;
}
5、叶子结点数
int LeadCount(BiTree T){
if(T == NULL) return 0;
if(T->lchild == NULL && T->rchild == NULL) return 1;
else return LeafCount(T->lchild) + LeafCount(T->rchild)+1;
}
******************************
线索二叉树
二叉树链表中空指针域的数量?
n个Node,2n个指针域,n个结点有n-1个孩子,
即2n个指针域中,有n-1个用来指示结点的左右孩子,其余n+1个指针域为空
若某个结点的左孩子为空,则将左指针域指向其前驱
若右孩子为空,则指向后继,加上了线索的二叉树叫做线索二叉树
*************************
树:是n(n>=0)个结点的有限集,n=0称为孔数空树
n>0,(1)有且只有一个特定的称为Root的结点;(2)其余结点课分为m(m>=0)个互不相交的有限集。
双亲表示法
利用结构数组实现
typedef struct PTNode{
TElemType data;
int parent;
} PTNode;
树结构
#define MAX_TREE _SIZE 100
typedef struct{
PTNode nodes[MAX_TREE_SIZE];
int r,n; //根结点的位置和结点个数
}PTree;
孩子链表
typedef struct CTNode{
//孩子结点结构
int child;
struct CTNode *next;
}*ChildPtr;
typedef struct{
//双亲结点结构
int data;
ChildPtr firstchild; //孩子链表头指针
}CTBox;
typedef struct{
//树结构
CTBox nodes[MAX_TREE_SIZE];
int n,r;//
}CTree;
还可以将上面两个结合起来,带双亲的孩子链表,就是给孩子链表前面+序号
孩子兄弟表示法(二叉树表示法,二叉链表示法)
二叉链表作为树的存储结构,
链表中的每个结点的两个指针分别指向其第一个孩子结点和下一个兄弟
typedef struct CSNode{
int data;
struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;
给我们一棵树,可以找到唯一的一棵二叉树与之对应
将树转换成二叉树:
(1)加线:在兄弟结点之间加一条线
(2)抹线:对每个结点,除了左孩子,去除与其余孩子的关系;
(3)旋转:顺时针45度
兄弟相连留长子
兄弟相连留长子
兄弟相连留长子
将二叉树转换成树:
(1)加线:若p结点是双亲结点的左孩子,
则将p的右孩子,右孩子的右孩子……
沿分支找到的所有有孩子,都与p的双亲用线连接
(2)抹线:抹掉原二叉树中双亲与右孩子之间的连线
(3)调整:按层次排列
左孩右右连双亲 去掉原来右孩线
左孩右右连双亲 去掉原来右孩线
左孩右右连双亲 去掉原来右孩线
*******************************************************
树的遍历
先根:若树不空,先访问根,然后依次先根遍历各课子树
后根:若树不空,先后根遍历各课子树,然后访问根
层次:若不空,则自上而下、自左至右访问
无中根,因为可以结点可以有好多个孩子
森林遍历