一.关于树的几个重要概念
1). 度:是指节点拥有的子树数。
2). 深度/高度:从根节点到目的节点x的一条简单路径的长度称为x的深度/高度。国内一般把根节点的高度算为1,而国外有些把根节点的高度算为0,因此计算起来会有差异。
对于4这个节点,度为1,深度/高度为2或3,整个树的高度/深度为3或4。
二.二叉树基本性质
二叉树定义:由n个节点组成(n>=0),构成空树,或者由两棵互不相交的称为左子树和右子树构成。
二叉树基本性质:
1> 在二叉树第i层最多有2^(i-1)个节点(i>=1)。
2> 深度为k的二叉树最多有2^k-1个节点(k>=1)。
3> 对于任何一棵二叉树T,终端节点数为n0,度为2的节点数为n2则n0=n2 + 1。
4> 具有n个节点的完全二叉树的深度为[log2n] + 1,
5> 如果对一棵有n个节点的完全二叉树(深度为[log2n] + 1)的节点按层序编号,对任一节点i(1<=i<=n)有:
(1). 如果i=1,则i是根节点,无双亲;否则其双亲是[i/2].
(2). 如果2i>n,则节点i无左孩子(节点i为叶节点);否则左孩子为2i.
(3). 如果2i+1>n,则节点i无右孩子(节点i为叶子节点);否则右孩子为2i+1.
三.二叉树基本操作
1>数据结构定义
typedef struct tagBinTreeNode
{
void* value;
struct tagTreeNode * left_child;
struct tagTreeNode * right_child;
}BinTreeNode;
2>二叉树的遍历是指从根节点出发,按造一定次序,访问二叉树的所有节点,使得每个节点被访问一次。D代表根节点,L代表左孩子,R代表右孩子。(1).前序遍历,DLR,即先访问根节点,再前序遍历左子树和右子树。
(2).中序遍历,LDR,即从根节点开始,中序遍历根节点的左子树然后访问根节点,最后中序遍历根节点的右子树。
(3).后序遍历,LRD,从左到右先子叶后节点的方式遍历访问左右子树,最后是根节点。
(4).层序遍历,从根节点开始逐层向下遍历,同一层从左到右逐个访问。
前序遍历算法:
void PreOrderTraverse(BinTreeNode *pstNode)
{
if(pstNode == NULL)
{
return;
}
printf("%c", (char)(pstNode->value));
PreOrderTraverse(pstNode->left_child);
PreOrderTraverse(pstNode->right_child);
}
中序遍历算法:
void InOrderTraverse(BinTreeNode *pstNode)
{
if(pstNode == NULL)
{
return;
}
PreOrderTraverse(pstNode->left_child);
printf("%c", (char)(pstNode->value));
PreOrderTraverse(pstNode->right_child);
}
后序遍历算法:
void PostOrderTraverse(BinTreeNode *ppstNode)
{
if(pstNode == NULL)
{
return;
}
PreOrderTraverse(pstNode->left_child);
PreOrderTraverse(pstNode->right_child);
printf("%c", (char)(pstNode->value));
}
层序遍历:void LerverOrderTraverse(BinTreeNode *pstNode)
{
if(pstNode == NULL)
{
return;
}
printf("%c", (char)(pstNode->value));
PreOrderTraverse(pstNode->right_child);
PreOrderTraverse(pstNode->left_child);
}
3>二叉树的建立
假设有这样一个二叉树:
void InOrderCreateBinTree(struct tagBinTreeNode **ppstNode)
{
char chr;
scanf("%c", &chr);
if(chr == '*')
{
*ppstNode = NULL;
}
else
{
(*ppstNode) = (struct tagBinTreeNode*)malloc(sizeof(struct tagBinTreeNode));
if((*ppstNode) == NULL)
{
exit(-1);
}
memset((*ppstNode), 0x00, sizeof(struct tagBinTreeNode));
InOrderCreateBinTree(&(*ppstNode)->left_child); //创建左子树
(*ppstNode)->value = chr; //构造根节点
InOrderCreateBinTree(&(*ppstNode)->right_child); //创建右子树
}
}
*注意, 虽然说是中序建立二叉树,但是字符的输入顺序应该按照前序列输入方式,设想如果先构造了左子树那么是无法退回父节点的。
四:测试
int main(int arrc, char* argv[])
{
struct tagBinTreeNode *pstNode = NULL;
InOrderCreateBinTree(&pstNode);
printf("\n前序输出:");
PreOrderTraverse(pstNode);
printf("\n中序输出:");
InOrderTraverse(pstNode);
printf("\n后序输出:");
PostOrderTraverse(pstNode);
printf("\n层序输出:");
LevelOrderTraverse(pstNode);
printf("\n");
return (0);
}
输出:
lc@lc-Lenovo:~/work/ProgramTrain$ ./a.out
AB*D**C**
前序输出:ABDC
中序输出:BDAC
后序输出:BDCA
层序输出:ACBD