二叉树的定义(递归定义)[Binary Tree]:它是n(n>=0)个节点的有限集合,当n=0时,它是空集,n<>0时,它由一个根节点和两棵互不相交的子二叉树构成,分别称为左、右子树。
二叉树有五种基本形态:(1)空集;(2)左子树为空;(3)右子树为空;(4)左右皆为空;(5)左右皆不为空。
有上面二叉树的定义知,每一个二叉树中的节点有如下特征:(1)至多两个子节点;(2)节点有左右之分;(3)有一个左节点活又节点;(4)没有子节点。
二叉树与树、有序树的不同点: 二叉树与度数不超过不超过2的树和有序树不同。在有序树中,虽然也有左右之分,但如果一个节点只有一个子节点,就无须区分左右节点。而在二叉树中,即使一个节点也是区分左右的,
二叉树遍历(Traversal) 二叉树的遍历就是沿着某条搜索路径,一次对树中的每个节点访问且仅访问一次。由二叉树的递归定义知,我们可以用递归方法遍历:
a. 访问树的根节点(N--root Node);
b. 遍历根节点的左子树(L–Left);
c.遍历根节点的右子树(R);
由于二叉树是“L/NR” 结构,所以我们可以有如下可能的遍历次序:LNR, LRN; NLR, || NRL; RLN, RNL, 对这个序列进行调整,
LNR, LRN, NLR, || RLN, NRL,RNL, 这时我们就会发现这个序列关于 "||" 轴对称,也就是一边的是另一边的遍历的倒序。所以,我们只需要研究其中的三种就可以了。依据根节点被访问的次序,我们就可以得到前序遍历 NLR(Preorder Traversal), 中序遍历LNR(Inorder Traversal), 后序遍历LRN(Postorder Traversal).
下面我们来看看二叉树的存储问题。在这里,我们现不研究它如何在硬盘/文件中的存储方式,先研究如何把它用数据结构方式存储到内存中,方便我们的操作。
二叉树的存储(内存中的结构)对于二叉树,它的结构由以下三个要素构成:(1)节点;(2)节点的连接关系;(3)父节点相同时子节点的左右次序。知道了这三个要素,我们也就知道了我们需要设计出一种结构来保存这三个元素,但此时,我们还要多想一点,这三个元素中哪哪一个是主导元素呢? 是节点?边?左右次序? 这时也许要迷惑了,为什么要想这么多呢? 请注意,虽然它自身在定义时是以节点为主体来定义的,但二叉树是一种抽象的结构,它不仅仅可以存储节点(一般被用来保存数据),同时也保存了边(一般保存了节点之间的关系,如父子关系。),所以,以何为主体完全可以由自己的实机需要来决定。
下面我们先假设我们认为主体是节点(这也是数据结构教科书中常常默认的),也就是我们要处理的数据,那我们可以设计出下面的结构,
/*****************************
* binary tree node definition
*****************************/
typedef struct binaryNode{
T *data; /* T is the type you stored in node. data is the data in the node.*/
struct binaryNode *left; /* left node of parent node. */
struct binaryNode *right; /* right node of parent node. */
}BinaryNode;
由前面遍历的递归描述我们可以得到下面的递归遍历算法,
/*********************
*binarytree.h
**********************/
#include
#include
#include
/*****************************
* binary tree node definition
*****************************/
typedef struct binaryNode{
T *data; /* T is the type you stored in node. data is the data in the node.*/
struct binaryNode *left; /* left node of root. */
struct binaryNode *right; /* right node of root. */
}BinaryNode;
/********************************
* Inorder binary tree traversal.
********************************/
void inorder(BinaryNode* root)
{
if (root != NULL) {
inorder(root -> left); /* recur left */
printf("%c ", root -> data);
inorder(root -> right); /* recur right */
}
}
/*********************************
* Preorder binary tree traversal.
**********************************/
void preorder(BinaryNode* root)
{
if (root != NULL) {
printf("%c ", root -> data);
preorder(root -> left);
preorder(root -> right);
}
}
/***********************************
* Postorder binary tree traversal.
***********************************/
void postorder(BinaryNode* root)
{
if (root != NULL) {
postorder(root -> left);
postorder(root -> right);
printf("%c ", root -> data);
}
}
这个递归遍历算法看起来很简单,但效率不会太高。因为递归调用会倒置没递归一次都需要在栈中保存一个函数指针,而系统的栈的空间大小却是非常有限的(一般是 1M到几M),所以,当数据很多时就可能出现stack over flow的错误。所以,这个递归算法通常在小数据量的情形下是可行的,太多的数据时就得考虑用非递归算法了。
接下来,我们就考虑用非递归方式实现遍历。可参考http://blog.csdn.net/rainer7/archive/2004/08/10/70671.aspx
作者:豆博草堂