1、对于任何一种树,如果只有根的话,算是有1个结点或者是有一个叶子。
2、层数/深度是从根开始算的
3、对于任何一颗树而言
(1)在二叉树的第 i 层上至多有2的(i-1)个结点
(2)深度为 k 的二叉树上至多含2的k-1个结点(k≥1)
根节点的层次为1,如果只有根节点,那么深度也为1;
(3)对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为 2 的结点,则必存在关系式:n0 = n2+1
4、对于完全二叉树而言
最多需要的比较次数也就是树的高度这么多
(1)完全二叉树的深度为 log2底n的整数值 +1 。
(2)若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:
- 若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 i/2取整数值部分的结点为其双亲结点;
- 若 2i>n,则结点i无左孩子, 否则,编号为 2i 的结点为结点i的左孩子结点;
- 若 2i+1>n,则结点i无右孩子结点,否则,编号为2i+1 的结点为结点i的右孩子结点。
5、树转化为二叉树或者是森林转化为二叉树
树/森林对应的二叉树,其左、右子树的概念已改变为: 左孩子,右兄弟
注意:森林中的两个兄弟结点,互相之间,可能是没有关系的。对于二叉树和森林来说,左孩子,右兄弟。
6、先、中、后序遍历是对于根而言的
7、树遍历的三种途径
- 先根(次序)遍历:若树不空,则先访问根结点,然后依次先根遍历各棵子树。(先总根,然后每个分支的根,然后每个分支的左分支,然后每个分支的右分支。)
- 后根(次序)遍历:若树不空,则先依次后根遍历各棵子树,然后访问根结点。(先不访问总根,在孩子中,从左向右依次访。问先访问每个分支的左,每个分支的右孩子,然后再访问每个分支的根(大体是这个思路,但是,有的时候在孩子里面还有分支))
- 按层次遍历:若树不空,则自上而下自左至右访问树中每个结点。
8、森林的遍历
- 先序遍历:依次从左至右对森林中的每一棵树进行先根遍历。
- 中序遍历:依次从左至右对森林中的每一棵树进行后根遍历。
9、森林、树、二叉树遍历方式的比较
10、树的类型
(1)满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点二叉树。
(2)完全二叉树(满足下面两个条件)
第一种
- 一棵二叉树只有倒数第二层上的结点的度数(每个结点有几个分支,度数即为几)可以小于2,其余每层上的结点的度数都等于2。
- 空缺在右边(只要不是左边就行,不一定必须在相对于根节点来说的左子树上)
第二种是
- 满二叉树
下图中左边是完全二叉树,右边不是完全二叉树
(3)平衡二叉树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
(4)二叉搜索树(二叉排序树)将未排序的序列,按照“左小右大”的规律插入组建树
折半查找判定树一定是平衡二叉树,但是二叉搜索树不一定是平衡二叉树,虽然两者都是左小右大
折半查找判定树和二叉搜索树的中序遍历是按照从小到大的顺序排列
(1)若其左子树非空,则左子树上所有结点的值均小于根结点的值;
(2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
(3)其左右子树本身又各是一棵二叉排序树
(4)左小右大(左小右大指的是,左边的所有都要比根节点小,右边的所有都要比根节点大,是二叉树,每个节点只有左右两个孩子)
第一种
- 是一棵空树
第二种
- 如果不为空,那么其左子树节点的值都小于根节点的值;右子树节点的值都大于根节点的值
- 其左右子树也是二叉搜索树
二、折半查找判定树(折半查找判定树一定是一棵平衡树)先将一组数排好序,然后再折半插入树
https://blog.csdn.net/u011240016/article/details/52923559
(1)折半查找判定树是一棵二叉排序树,它的中序遍历结果是一个升序序列,可以在选项中的树上依次填上相应的元素。虽然折半查找可以向上取整(11/2=5.5,向上取整之后为5)也可以下取整(11/2=5.5,向下取整之后为6),但是一个查找判定树只能有一种取整方式。
(2)折半查找判定树特点(左小右大)(左小右大指的是,左边的所有都要比根节点小,右边的所有都要比根节点大,是二叉树,每个节点只有左右两个孩子):右子树中各结点值比根大,左子树中各结点值比根小,子树也如此。根据判定树可如下完成查找:从根结点开始,比当前元素大就向右,小则向左,或者找到,或者到空.。
(3)判定是否为折半查找判定树的方法:
设一共有n个数,下面每个步骤要么全是向上取整,要么全是向下取整
- 中间数是(1+n)/2 (其实也可是是(0+(n-1))/2,但是为了方便起见需要看,到底是数字从0开始还是从1开始,如果从0开始就用(0+(n-1))/2,如果从1开始就用(1+n)/2 )
- 左小右大
- 之后不断重复上述1、2步骤
(4)对于折半查找判定树
- 要么对于“每个根节点”来说,左右结点的个数都相同
- 要么对于“每个根节点”来说,要么左结点数大于右结点数,要么左右结点的个数都相同
- 要么对于“每个根节点”来说,要么左结点数小于右结点数,要么左右结点的个数都相同
- 左右结点大小的比较是统一的,如果不是统一的话,就一定不是折半查找判定树
- 但是如果每一层上都统一,不能说明一定是折半查找判定树,还要看具体的数据
11、叶子结点不管是在哪种遍历方法中,它们的相对位置是不变的。先序遍历、中序遍历、后序遍历三个其实是相对根来说的,叶子结点左右遍历的顺序是不变的。
只有两个结点的树度为1
12、
13、
14、
15、
有好多种二叉树,它的度不一定为2,所以,二叉树的度为2是错误的。至于是否可以任意交换,还需要看树是什么类型的。
16、对于任何一棵树,结点总数=总分支数目+1
结点数=森林边数+森林包含树的棵数
17、
18、先序:根左右,若某一个结点既是左结点/右结点,同时,也是根节点的话,先看以其为根节点的子树的根左右。
19、
20、
21、
22、
(1)普通二叉树具有的性质
- 在二叉树的第 i 层上至多有2i-1个结点
- 深度为 k 的二叉树上至多含2k-1个结点(k≥1)
- 对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为 2 的结点,则必存在关系式:n0 = n2+1
(2)完全二叉树才拥有的性质
- 具有 n 个结点的完全二叉树的深度为 log2底n的整数值 +1
- 若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:
若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 i/2取整数值部分的结点为其双亲结点;
若 2i>n,则结点i无左孩子, 否则,编号为 2i 的结点为结点i的左孩子结点;
若 2i+1>n,则结点i无右孩子结点,否则,编号为2i+1 的结点为结点i的右孩子结点。
(3)
- 设二叉树中度为0的叶子结点个数为n0,度为1结点个数为n1,度为2结点个数为n2,于是n0 + n1 + n2 = 1102。
- 根据二叉树性质:n0 = n 2 + 1,代入得到,2n2 + n1+1 = 1102
- 由于完全二叉树的n1 只能是0或者1(为什么完全二叉树中度为1的结点只能是1或0?如果从满二叉树中在最后一层自左向右砍掉的节点数是偶数,那么该完全二叉树中度为1的节点数就是0。如果砍掉的节点数是奇数,那么该完全二叉树中就有且仅有一个节点的度为1. )
- 为满足2n2 + n1 = 1101,n1 =1,因此n2 = 550。
- 由n0 = n 2 + 1得n0 = 551,即叶子个数是551个。
23、
24、
25、
26、
27、带权路径长度(WPL)
法一:带权路径长度=叶子的权数*叶子路径的长度+ 叶子的权数*叶子路径的长度 所有叶子这样操作之后求和 ; WPL(T) = 求和wklk (对所有叶子结点)
法二:对于所有的树还有另外的算法就是:一棵树的带权路径长度等于其中所有分支结点的权值之和,也就是除头结点外各节点的权值加和
28、哈夫曼树细碎知识点
- 对N(≥2)个权值均不相同的字符构造哈夫曼树,则树中任一非叶结点的权值一定不小于下一层任一结点的权值
- 哈夫曼树中一定没有度为1的结点
- 哈夫曼树中两个权值最小的结点一定是兄弟结点
- 经过哈夫曼编码后,求文本所占字节数也就是求带权路径长度
- 等长方式编码:有n个字符就是log2底n的长度,在求n个字符的带权路径长度的时候,叶子结点的路径长度均为log2底n
- 哈夫曼编码比采用等长方式的编码节省了多少位数,也就是求出各自的带权路径长度之后做差
- 一棵哈夫曼树的带权路径长度等于其中所有分支结点的权值之和,也就是除头结点外各节点的权值加和
- 哈夫曼树是带权路径长度最短的树,路径上权值较大的结点离根较近
- 哈夫曼树的结点个数不能是偶数
- 哈夫曼树是n个带权叶子结点构成的所有二叉树中带权路径长度最小的二叉树
- 在哈夫曼树中,任何一个结点它的度都是0或者2,没有度数为1的结点,结点数一定是奇数个,不能是偶数个。
- 当一棵具有n 个叶子结点的二叉树的WPL 值为最小时,称其树为哈夫曼树,其二叉树的形状不是唯一的
- 编码:左0右1,从树根到叶结点,经历的分支连起来就是编码序列
- 某字符出现的概率/频率为,可以将概率视为叶子的权值
- 哈夫曼树形状不止一种,有多种,但是带权路径长度一定是最小的。
- 哈夫曼树不一定是一棵完全二叉树
29、
30、
31、
如果根节点有右孩子,次右孩子只有左孩子的话,就是在右子树的某个右孩子上,而不是叶子结点
所有结点的平均查找效率是O(logN)
32、
插入方法是从上往下一个个判断的,而不是说只能从最下面插进去,92便是一个很好的例子。
33、
34、
35、
36、
A:删除之后,再重新插入的话,总是插入到最下层,作为新的叶子结点,所以是错误的
B:不一定,要求所有结点,不只是对于这个结点而言,而是对于所有的结点都应当有这样的性质
C:顺序不同,生成的搜索树就是不一样的,因为,插入的每一个新的结点,总是插入到最下层,作为新的叶子结点
37、
答案:T
38、二叉树的遍历 (25 分)
本题要求给定二叉树的4种遍历。
函数接口定义:
void InorderTraversal( BinTree BT );
void PreorderTraversal( BinTree BT );
void PostorderTraversal( BinTree BT );
void LevelorderTraversal( BinTree BT );
其中BinTree
结构定义如下:
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
要求4个函数分别按照访问顺序打印出结点的内容,格式为一个空格跟着一个字符。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
typedef char ElementType;
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
BinTree CreatBinTree(); /* 实现细节忽略 */
void InorderTraversal( BinTree BT );
void PreorderTraversal( BinTree BT );
void PostorderTraversal( BinTree BT );
void LevelorderTraversal( BinTree BT );
int main()
{
BinTree BT = CreatBinTree();
printf("Inorder:"); InorderTraversal(BT); printf("\n");
printf("Preorder:"); PreorderTraversal(BT); printf("\n");
printf("Postorder:"); PostorderTraversal(BT); printf("\n");
printf("Levelorder:"); LevelorderTraversal(BT); printf("\n");
return 0;
}
/* 你的代码将被嵌在这里 */
输出样例(对于图中给出的树):
Inorder: D B E F A G H C I
Preorder: A B D F E C G H I
Postorder: D E F B H G I C A
Levelorder: A B C D F G I E H
答案:
void InorderTraversal( BinTree BT )
{
if(BT)
{
InorderTraversal(BT->Left);
printf(" %c",BT->Data);
InorderTraversal(BT->Right);
}
}
void PreorderTraversal( BinTree BT )
{
if(BT)
{
printf(" %c",BT->Data);
PreorderTraversal(BT->Left);
PreorderTraversal(BT->Right);
}
}
void PostorderTraversal( BinTree BT )
{
if(BT)
{
PostorderTraversal(BT->Left);
PostorderTraversal(BT->Right);
printf(" %c",BT->Data);
}
}
void LevelorderTraversal( BinTree BT )
{
if(BT)
{
BinTree a[100000];
BinTree b;
int tail=0,head=0;
a[tail++]=BT;
while(head!=tail)
{
b=a[head++];
printf(" %c",b->Data);
if(b->Left!=NULL) a[tail++]=b->Left;
if(b->Right!=NULL) a[tail++]=b->Right;
}
}
}