常用术语
- 根:二叉树是由唯一的起始结点引出的结点集合,这个起始结点称为根。
- 父结点(双亲):任何非根结点都有且仅有一个前驱结点,称为该结点的父结点(双亲)。根结点没有父结点。
- 左/右子结点:二叉树的任何结点最多可能有两个后继结点,分别称为左/右子结点(或左/右海子)。具有相同父结点的结点之间称为兄弟结点。
- 结点的度:二叉树中一个结点的子树数目称为该结点的度。
- 叶结点:没有子结点的结点称为叶结点。叶结点的度为0。除叶结点外的那些非终端结点称为内部结点(或分支结点)
- 边:父结点k与子结点k'之间存在一条有向线段<k,k'>,称作边。
- 路径:两个结点间相连边的集合,该路径所经历的边的个数称为这条路径的路径长度。
- 结点的层数:从根结点到某个结点的路径长度称为结点的层数,根结点为第0层,非根结点的层数是其父结点的层数加1。
- 树的深度:层数最大的叶结点的层数称为树的深度。
- 树的高度:树的深度加1为树的高度。
- 子树:切断一个结点与其父结点的连接,则该结点与其子孙构成的树就称为以该结点为根的子树。
满二叉树和完全二叉树
满二叉树:一棵二叉树的任何结点,或者是树叶,或者左右子树均非空。
完全二叉树:除最后一层外,每一层上的结点数均达到最大值,最后一层上只缺少右边的若干结点。

二叉树的性质
性质1:二叉树的第i层上至多有个结点。(根结点为第0层)
性质2:深度为k的二叉树至多有个结点。
性质3:在任意二叉树中,若叶子结点(即度为0的结点)个数为n0,度为1的结点个数为n1,度为2的结点个数为n2,那么n0 = n2 + 1。
性质4:满二叉树定理:非空满二叉树的树叶数目等于其分支结点数加1。
性质5:满二叉树定理推论:一个非空满二叉树的空子树数目等于其结点数加1。
性质6:具有n个结点的完全二叉树深度为,高度为
。
性质7:若对于有n个结点的完全二叉树进行顺序编号,那么对于编号为i的结点:
父亲:
如果i = 0 ,则结点i是二叉树的根,无双亲;
如果i > 0 ,则其父结点编号是;
孩子:
当2i+1<=n-1时,结点i的左孩子是2i+1,否则结点i没有左孩子。
当2i+2<=n-1时,结点i的右孩子是2i+2,否则结点i没有右孩子。
二叉树的储存顺序
顺序存储和链式存储
1.顺序存储结构
对于完全二叉树,从根结点起,自上而下,从左至右存储,即按顺序编号存储。根据完全二叉树性质,对于任意一个结点,若它存储在第i个位置,则它的左右子结点存放在2i+1和2i+2的位置,其父结点的下标为。
顺序存储是完全二叉树最简单、最节省空间的一种存储方式。
对于一般二叉树,也必须按照完全二叉树的形式来存储树中的结点。
2.链式存储结构
根据结点结构的不同,有两种方法:二叉链表、三叉链表。
struct node
{
struct node *lchild;
char c;
struct node *rchild;
}Node,*Pnode;
struct node
{
struct node *lchild;
char c;
struct node *parent;
struct node *rchild;
};
二叉树的遍历
二叉树的遍历分为:深度优先遍历和广度优先遍历
深度优先遍历
前序遍历、中序遍历、后续遍历
根左右
左根右
左右根
1)前序遍历
(1)访问根结点;
(2)按前序遍历左子树;
(3)按后序遍历右子树;
2)中序遍历
(1)按中序遍历左子树;
(2)访问根结点;
(3)按后续遍历右子树;
3)后续遍历
(1)按后序遍历左子树;
(2)按后序遍历右子树;
(3)访问根结点;
线索二叉树
在n个结点的二叉树中,必定有n+1个空链域
含有n个结点的二叉链表中,链域一共有2*n个(每个点有两个链域)。
对于除了根结点以外的每个点都是有一个父亲结点,所以一共有n-1个指针指向某个结点,
于是形成n-1个有内容的链域(减1即是父亲结点)所以一共有2*n-(n-1)=n+1个链域没有指向任何东西。
- 若结点的左子树为空,则该结点的左孩子指针指向其前驱结点
- 若结点的右子树为空,则该结点的右孩子指针指向其后继结点
这种指向前驱和后继的指针称为线索,将一棵普通的二叉树以某种次序遍历,并添加线索的过程称为线索化。
- ltag==0,指向左孩子;ltag==1,指向前驱结点
- rtag==0,指向右孩子;rtag==1,指向后继结点
#include<stdio.h>
#include<stdlib.h>
//二叉树结构的定义
typedef struct BinaryTreeNode{
int m_nValue;
struct BinaryTreeNode *m_pLeft;
struct BinaryTreeNode *m_pRight;
}BinaryTreeNode;
//构建二叉树的核心函数,前序的子串起止位置和中序的子串起止位置
BinaryTreeNode * ConstructCore(int *startPreorder, int *endPreorder, int *startInorder, int *endInorder);
//构建二叉树的函数,前序遍历和中序遍历的起始位置与长度
BinaryTreeNode *Construct(int *preorder, int *inorder, int length)
{
if(preorder == NULL || inorder == NULL || length <= 0) {
printf("invalid input!\n");
return 0;
}
return ConstructCore(preorder, preorder + length -1, inorder, inorder + length - 1);
}
BinaryTreeNode *ConstructCore(int *startPreorder,int *endPreorder,int *startInorder,int *endInorder)
{
//前序遍历的第一个元素,即为根节点
int rootValue = startPreorder[0];
//构建根节点
BinaryTreeNode * root = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
root->m_nValue = rootValue;
root->m_pLeft = root->m_pRight = NULL;
//顺序遍历到最后一个元素
if (startPreorder == endPreorder)
{
//该节点满足是根节点的条件如括号中所示
if (startInorder == endInorder && *startPreorder == *startInorder) {
return root;
} else {
printf("invalid input!\n");
}
}
//根据前序遍历中的根节点的值,在中序遍历中找到根节点
int* rootInorder = startInorder;
while(rootInorder <= endInorder && *rootInorder != rootValue) {
++rootInorder;
}
if(rootInorder == endInorder && *rootInorder != rootValue) {
printf("invalid input!\n");
}
//在中序数组中找到它的左子树的长度
int leftLength = rootInorder - startInorder;
//根据中序遍历中左子树的长度在前序遍历中找到左子树的位置
int* leftPreorderEnd = startPreorder + leftLength;
if(leftLength > 0){
//构建左子树,输入为前序遍历子串的起始地址,中序遍历的起始地址
root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1);
}
if(leftLength < (endPreorder - startPreorder))
{
//构建右子树,输入为前序遍历子串的起始地址,中序遍历的起始地址
root->m_pRight = ConstructCore(leftPreorderEnd + 1,endPreorder, rootInorder + 1, endInorder);
}
return root;
}
void lastOrderTraverse(BinaryTreeNode *root)
{
if(root)
{
lastOrderTraverse(root->m_pLeft);
lastOrderTraverse(root->m_pRight);
printf("%d ",root->m_nValue);
}
}
int main()
{
int preorder[8] = {1, 2, 4, 7, 3, 5, 6, 8};
int inorder[8] = {4, 7, 2, 1, 5, 3, 8, 6};
int length = sizeof(preorder) / sizeof(int);
BinaryTreeNode *tree = Construct(preorder, inorder, length);
lastOrderTraverse(tree);
return 0;
}
堆
堆是一种特殊类型的二叉树,具有以下两个性质:
(1)每个节点的值大于(或小于)等于其每个子节点的值;
(2)该树完全平衡,其最后一层的叶子都处于最左侧的位置。 满足上面两个性质定义的是大顶堆(max heap)(或小顶堆min heap)。这意味着大顶堆的根节点包含了最大的元素,小顶堆的根节点包含了最小的元素
由于堆是一个完全树,一般采用数组实现,对于一个下标为i的结点:
其父结点下标为:(i-1)/2
其子结点下标为:2i+1,2i+2
- heap[i] ≧heap[2*i+1]
- heap[i] ≧heap[2*i+2]