下图是一棵典型的树
数据结构中的树长这样:(不像树?倒过来看看?)
一棵树是由n(n>0)个元素组成的有限集合,其中:
结点:每个元素称为结点(node)
根:每棵树有一个特定的结点,称为根结点(root),如上图的结点 ①
子树:除根结点外,其余结点能分成多个互不相交的有限集合,其中每个子集又都是一棵树,这些集合称为这棵树的子树。
性质一:一棵树中的任意两个结点有且仅有唯一的一条路径连通。
性质二:一棵树如果有n个结点,那么它一定恰好有n-1条边
兄弟结点:上图中的结点 ② ③ ④ 就是兄弟结点
父结点:结点 ① 是结点 ② ③ ④ 的父亲结点
结点的度:该结点的子树数量。上图中,一号结点到九号结点的度分别为3、2、0、1、0、0、2、0、0
树的度:树中所有结点最大的度数
叶子结点:无分支,度为0的结点,如③ ⑤ ⑥ ⑧ ⑨
分支结点:度不为0的结点
树的深度:树中最大层数(上图的树深度为4)
森林:由数棵互不相交的树组成
二叉树 —— Binary tree简写BT,是一种特殊的树型结构,二叉树的每个结点最多有两个子结点。每个结点的子结点分别称为左孩子、右孩子,它的两棵子树分别称为左子树、右子树。二叉树有5中基本形态:
(1) 空二叉树
(2) 仅有根节点的二叉树
(3) 右子树为空的二叉树
(4) 左右子树均不空的二叉树
(5) 左子树为空的二叉树
满二叉树:除了叶结点外每一个结点都有左右子女且叶结点都处在最底层的二叉树。
完全二叉树:在满足满二叉树的性质后,最后一层的叶子节点均需在最左边。完全二叉树中度数为1的结点至多有1个。
对上图的完全二叉树补充几个结点,可以成为另一棵完全二叉树
完全二叉树的反例:
上图不是完全二叉树,缺了10号那个位置(人话:中间不能留空,把11号和12号改成10号和11号也不行)
性质1:在二叉树的第i层上最多有2^(i-1)个结点(i>=1)性质2:深度为k的二叉树至多有2^k – 1个结点(k>=1)性质3:对任意一棵二叉树,如果其叶子结点数为n0,度为2的结点数为n2,则一定满足:n0 = n2 + 1性质4:具有n个结点的完全二叉树的深度 k = floor(log2n) + 1(以2为底n的对数,下取整)性质5:对于一棵n个结点的完全二叉树,对任一个结点(编号为i),有:
① 如果i = 1,则结点i为根,无父结点;如果i > 1,则其父结点编号为i / 2
② 如果2 x i > n,则结点i无左孩子,当然也无右孩子,即结点i为叶子结点,否则左孩子编号为2 x i
③ 如果2 x i + 1 > n,则结点i无右孩子,否则右孩子编号为
2 x i + 1
性质的证明
第一和第二个性质举例理解即可,下面证明一下第三个性质:
(没记错的话这个证明是中国矿业大学的研究生初始试题)
从结点的角度:因为二叉树中所有结点的度数均不大于2,不妨设n0表示度为0的结点个数,n1表示度为1的结点个数,n2表示度为2的结点个数。三类结点的个数加起来为总结点个数,于是便可得到:总结点的个数为n0 + n1 + n2 —— (1)
从边的角度:度为2的结点可以产生两条边,同理,度为1的结点可以产生一条边,度为0的结点不产生边,由此关系可以得到边的条数为:
n0 x 0 + n1 x 1 + n2 x 2 —— (2)
在之前已经介绍了一个性质,一棵树如果有n个结点,那么它一定恰好有n-1条边
根据性质,联立(1)(2),可得结点数减一等于边数,即:
n0 + n1 + n2 - 1 = n0 x 0 + n1 x 1 + n2 x 2
化简得
n0 = n2 + 1二叉树的存储
存储方式一:顺序存储结构
下图是一棵完全二叉树
开一个字符数组:char a[100]
可以利用字符数组a存储二叉树中的信息
顺序存储结构既有其优点,也有其不足之处
优点:方便获取父节点和左、右结点
如:想要获取E结点的父亲结点,只需要访问a[5/2]即可找到B结点
再如:想获取结点C的左孩子,只需要访问a[3x2]即可找到F结点缺点:对于非完全二叉树,较浪费空间
什么是非完全二叉树?
如去掉二叉树中的3、6、7、8、9号结点,存储时仍需人为补上,耗费空间
存储方式二:链式存储结构
typedef struct node { int data; struct node *lchild; struct node * rchild; }BTNode,*tree; tree root = NULL;
对于上图的树,链式存储结构为
先序遍历(DLR):
若二叉树为空,则空 *** 作,否则
① 访问根结点
② 先序遍历左子树
③ 先序遍历右子树
中序遍历(LDR):
若二叉树为空,则空 *** 作,否则
① 中序遍历左子树
② 访问根结点
③ 中序遍历右子树
后序遍历(LRD):
若二叉树为空,则空 *** 作,否则
① 后序遍历左子树
② 后序遍历右子树
③ 访问根结点
例:写出以下二叉树的先序、中序、后序遍历
对于以上二叉树
先序遍历为(根左右) : ABFC
中序遍历为(左根右) : BAFC
后序遍历为(左右根) : BCFA
例:写出以下二叉树的先序、中序、后序遍历
对于以上二叉树
先序遍历为(根左右) : ABCF
中序遍历为(左根右) : CBAF
后序遍历为(左右根) : CBFA
例:写出以下二叉树的先序、中序、后序遍历
对于以上二叉树
先序遍历为(根左右) : ABCDEFG
中序遍历为(左根右) : CBEDAFG
后序遍历为(左右根) : CEDBGFA
#includeusing namespace std; typedef struct node { int data;//数据域 struct node *lchild;//左孩子 struct node *rchild;//右孩子 }BTNode,*tree;//使用typedef将结构体重命名为BTNode,BTNode = struct node tree root;//根结点 //以上结构体的等价写法 //创建二叉树 void creat(tree &root) { int x; cin >> x; tree bt; if (x != -1)//-1表示空树 { bt = new node;//new一片空间,定义一个指针指向它 bt -> data = x;//建立根结点 creat(bt -> lchild);//递归建立右子树 creat(bt -> rchild);//递归建立左子树 } else bt = NULL;//x为-1表示当前的树为空树 } void DLR(tree root)//先序遍历:根->左->右 { if (root == NULL) return;//递归出口 cout << root -> data;//根结点 DLR(root -> lchild);//遍历左子树 DLR(root -> rchild);//遍历右子树 } void LDR(tree bt)//中序遍历:左->根->右 { if(bt == NULL) return; LDR(bt -> lchild);//遍历左子树 cout << bt -> data;//根结点 LDR(bt -> rchild);//遍历右子树 } void LRD(tree bt)//后序遍历 { if(bt == NULL) return; LRD(bt -> lchild);//遍历左子树 LRD(bt -> rchild);//遍历右子树 cout << bt -> data;//根结点 } int main() { creat(root); DLR(root); return 0; }
需要注意的是,通过输入AB##F#C##可得到如图所示的二叉树
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)