1.树的一个很重要的特点是不会形成回路
2.结点的度:该结点拥有的子树的数目
3.树的度:树中结点度的最大值
4.叶节点:度为0的结点
5.分支节点:度非0的结点(也就是在深度大于1的树中的非叶子节点)
6.树的层次:根节点的层次为第1层
7.树的深度:树中节点所处的最大数
8.树的度和结点的度是两个概念,树的度是最大的结点度
9.二叉树是有序树,因为它要区分左子树和右子树。反正就是,子树有严格的左右之分且度≤2的树才是二叉树
10.具有3个结点的二叉树有5种形态,因为二叉树是有序的:
11.具有3个节点的树只有一种形态:
1)满二叉树:
就是满满的二叉树
2)完全二叉树:
只有最后一层不是满的,其它层都是满的,而且在最后一层中,这种“不满”是从左到右的;
而下面这棵树,由于它的不满不是从左到右的,所以它不是完全二叉树
但是要注意的是,完全二叉树中不是最后一层才是叶子节点,叶子节点可以在倒数第二层
今天发现一个事,就是如果完全二叉树中子节点个数为1的节点最多只有1个,且这时候完全二叉树的总节点个数为偶数;如果二叉树总结点个数为奇数,那说明这棵树没有子节点个数为1的节点
3)平衡二叉树:每一节点的两棵子树的最大高度差不超过1
4)真二叉树:每个分支节点都有两个孩子节点
13.深度为h的非空二叉树最多有2h-1个结点,深度为h的完全二叉树至少有2h-1个结点
14.若非空二叉树有n0个叶结点,有n2个度为2的结点,则n0=n2+1
15.二叉树的一些性质:
16.前中后序遍历二叉树,主要是根据什么时候遍历根节点来看的
17.利用前+中序序列或者后+中序序列都是能够恢复二叉树的,而仅有前+后序序列是没办法恢复二叉树的,因为你看下面这两棵树,它们的前序、后序序列都是一样的:
18.在前序/后序序列中确定根,在中序序列中分左右
19.树拷贝时先拷贝当前结点,再拷贝子节点;树删除时先删除子节点,再删除当前结点
20.什么是线索二叉树:利用二叉链表中空的指针域指出结点在某种遍历序列中的直接前驱或直接后继,指向前驱和后继的指针称为线索,加了线索的二叉树称为线索二叉树。
21.线索二叉树的构造:利用链结点的空的左指针域存放该节点的直接前驱地址,空的右指针域存放该节点的直接后继的地址,而非空的指针域仍然存放节点的左孩子或右孩子的地址。
22.0表示线索,1表示孩子
23.分支节点:度不为0的节点是分支节点
24.前缀编码:任意一个字符的编码不能是另一个字符的编码的前缀
25.严格二叉树:如果一棵二叉树的每个非终端节点有且仅有两棵子树,那称这棵二叉树为严格二叉树
26.给出度为XXX的节点有XXX个,求叶结点/总结点个数的题,使用这个公式就可以了:
节点个数=分支个数+1
27.m阶树,指每个分支节点至多有m棵子树
28.m阶B-树是指满足以下几个条件的m叉树:
1)每个分支节点最多有m棵子树
2)除根节点外,每个分支节点最少有m/2向上取整
棵子树
3)根节点至少有两棵子树(除非根为叶节点,此时B-树只有一个节点)
4)所有叶结点都在同一层上,叶结点不包含任何关键字信息(可以把叶结点视为实际上不存在的外部节点,指向这些叶结点的指针为空)
6)具有n棵子树的节点只有n-1个关键字
需要注意的是,B-树的每个节点的关键字不能超过m-1个,因为有m-1个关键字时,就有m个指向子树的指针了
29.一棵4阶B-树:
30.B+树:
前3个性质和B-树一致:
1)每个分支节点至多有m棵子树
2)除根节点外,每个分支节点最少有m/2向上取整
棵子树
3)根节点最少有两棵子树(除非根为叶结点,此时B+树只有一个节点
4)具有n棵子树的节点一定有n个关键字
5)叶节点中存放记录的关键字以及指向记录的指针,或者数据分块后每块最大的关键字值及指向该块的指针,并且叶结点按关键字值得大小顺序还会连成线性链表
6)所有分支节点可以看成是索引的索引,节点中仅包含它的各个孩子节点中最大/小关键字值和指向孩子节点的指针
一个实例:
31.B+和B-的区别:
1)B+有两个入口:根节点+叶结点形成的链表的头节点;B-有一个入口:根节点
2)B+是叶结点存放关键字,B-树是分支节点存放关键字,而且B-树的叶结点不包含任何信息,可以视为不存在
3)关键字和指针的数量关系
32.堆:堆是一棵完全二叉树,二叉树中任何一个分支结点的值都大于(或小于)或者等于他的孩子结点的值,并且每一棵子树都满足堆的特性。如果是大于,则称这样的堆为大顶堆;如果是小于,则称为小顶堆
33.一个基于前序遍历的在树中查找结点的算法
//结点的结构
typedef struct node{
int level;
char name[max];
struct node * lchild,*rchild,*parent;
}node;
//在树中找结点
nodeptr find_node(nodeptr np,char name[]){
nodeptr result=NULL;
if(np){
if(!strcmp(np->name,name))
result=np;
else{
result=find_node(np->lchild,name);
if(result==NULL)
result=find_node(np->rchild,name);
}
}
return result;
}
这个查找的方法还能用来给已有树的叶子节点增加子节点,只需要在找到相应节点后添上就行。
34.构建二叉查找树:
//结点的结构
struct node{
Datatype data;
Datatype level;
struct node *lchild,*rchild;
};
BTNodeptr insertBST(BTNodeptr p, Datatype item,Datatype level){
//printf("enter");
if(p==NULL){
p=(BTNodeptr)malloc(sizeof(BTNode));
p->data=item;
p->level=level;
p->lchild=p->rchild=NULL;
}
else if(item<p->data){
level+=1;
p->lchild=insertBST(p->lchild,item,level);
}
else{
level+=1;
p->rchild=insertBST(p->rchild,item,level);
}
return p;
}
使用:
scanf("%d",&item);
root=insertBST(root,item,1);
35.一棵完全二叉树如果以顺序存储的方式存在一个一维数组里,节点有n个,那么最后一个分支节点的下标为n/2-1
36.在二叉查找树中查找某个元素的效率与这棵树的深度有关。我容易错的是以为和这棵树的存储结构有关,这里我是把存储结构和节点的大小顺序和建立二叉查找树的根节点给搞混了,我是犯了因果颠倒的错误。实际上,二叉查找树建好后,它的形态就定下来了,不会因为是怎么存储、怎么建立这棵树而影响查找效率
37.如果想要在局部函数里为构建结点,比如在局部函数内构建根节点,那么就需要在局部函数内部申请空间,在主函数中想要获取这棵树,仅仅把根节点设为全局变量是不够的,还需要在局部函数中返回这个结点,因为局部函数申请的空间仅仅是存在于这个函数的栈中,函数结束后就会销毁这个栈,申请的内存空间也就跟着没了