树中,每一层上的数据元素可能和下一层中的多个元素相关,但最多智能和上一层中的一个元素相关。一个根结点分出m个子树。各节点关系以下图为例:

A是B的双亲,B是A的子女,<A,B>是从A到B的边。
B、C为兄弟,但G和H不是兄弟;
路径:A-B-F为一条路径,要求不断从父节点到子结点,路径长度为2(经过的边的个数)。
根节点层数为0,以此类推。树中结点最大的层数成为树的深度,上图深度为2。
一个结点子女的个数称为该结点的度数。度数为0的结点称为树叶,又叫终端结点;度数大于0的结点叫做分支结点。
二叉树
二叉树结点的子树有左子树、右子树之分,第i层结点数最多为2i;
深度为k的二叉树最多有2k+1 -1 个结点。
满二叉树:深度为k,且有2k+1 -1 个结点。
完全二叉树:最多只允许最下面两层上结点度数可以小于2,且最小面一层的结点都集中再该层最左边若干位置上,如下图所示。

链式存储结构
存储二叉树最直接的方式是链式存储结构。每个结点存储自身值,和两个指针域lchild、rchild,分别指向左子女和右子女。

//先来一个二叉树结点类的定义
class Node
{
public:
Node() :lchild(0), rchild(0){} //类Node有两个构造函数,第一个建立了一个lchild和rchild均为空的结点
Node(int val/*, Node *lptr, Node *rptr*/) //第二个建立一个叶或非叶结点,并用val初始化data。
{
data = val;
lchild = 0;
rchild = 0;
}
int data;
Node *lchild, *rchild;
};
或者更简单的如下:
struct BinaryNode
{
int m_nValue;
BinaryNode *m_pleft;
BinaryNode *m_pright;
}
二叉树的遍历
如果用D、L、R分别代表根结点、左子树、右子树。

先序遍历(DLR次序):若二叉树非空,访问根结点,先序遍历左子树,先序遍历右子树。
先输出根结点A,然后访问A的左子树,左子树根结点为B,输出B;
然后访问B的左子树根结点,输出D;D无左子树,遍历到右结点G,输出。
此时遍历完了B的左子树,继续B的右子树,输出E;
遍历完了A的左子树,继续A的右子树,根节点为C,输出;
C的左子树,输出F。 所以,最终输出 ABDGECF
中序遍历(LDR次序):若二叉树非空,依次,中序遍历左子树,访问根结点,中序遍历右子树。
不断划分左子树至D,没有左子树,输出父节点D,然后右结点G;
B的左子树结束,输出B,输出右结点E;
A的左子树结束,输出A,开始A的右子树;
C有左子树F,输出F,然后输出父节点C;
右子树遍历结束。最终输出 DGBEAFC
后序遍历(LRD次序):若二叉树非空,依次,后序遍历左子树,后序遍历右子树,访问根结点。
依旧是先不断找左子树,到D,没有左子树了,输出右子树G,然后是根结点D;
对B分出的左子树遍历完后,输出右子树E,然后是B;
A分出的左子树完成后,该右子树,C处只有左子树F,输出F后,输出D;
输出根结点A。 最终结果是: GDEBFCA
void Preorder(Node* ptr)
{
if (ptr != 0)
{
cout << ptr->data<<" "; //访问根结点
Preorder(ptr->lchild); //先序遍历左子树
Preorder(ptr->rchild); //先序遍历右子树
}
}
void Inorder(Node *ptr)
{
if (ptr != 0)
{
Inorder(ptr->lchild);
cout<< ptr->data << " ";
Inorder(ptr->rchild);
}
}
void Postorder(Node *ptr)
{
if (ptr != 0)
{
Postorder(ptr->lchild);
Postorder(ptr->rchild);
cout << ptr->data << " ";
}
}
附加一段测试代码:
int main()
{
Node *p1 = new Node(1);
Node *p2 = new Node(2);
Node *p3 = new Node(3);
Node *p4 = new Node(4);
Node *p5 = new Node(5);
p1->lchild = p2;
p1->rchild = p3;
p2->lchild = p4;
p2->rchild = p5;
Preorder(p1);
cout << endl;
Inorder(p1);
cout << endl;
Postorder(p1);
cout << endl;
delete p1, p2, p3, p4, p5;
system("pause");
return 0;
}
二叉树遍历的应用
(1)计算二叉树的深度
通过后序遍历,可以先得到左子树的深度dl,然后得到右子树的深度dr,则二叉树的深度为1+max{dl,dr}。设空二叉树的深度为-1.
(2)计算二叉树结点的个数
在遍历过程中,每个结点都要访问一次,只要计算出过程中访问的结点个数即可。
(3)计算二叉树叶子结点的个数
在遍历过程中,若发现某个结点的左右子树皆为空,则该 结点为叶子结点。
/**********************二叉树遍历的应用*****************************/
//计算二叉树深度(使用后续遍历思想)
int Depth(Node* ptr)
{
if (ptr == 0) return -1;
int dl =Depth(ptr->lchild);
int dr = Depth(ptr->rchild);
return 1+(dl>dr ? dl:dr);
}
//计算二叉树结点的个数
int Size(Node *ptr)
{
if (ptr == 0) return 0;
return 1 + Size(ptr->lchild) + Size(ptr->rchild);
}
//计算二叉树叶子节点的个数
int LeafCount(Node *ptr)
{
if (ptr == 0)return 0;
if (ptr->lchild == 0 || ptr->rchild == 0)return 1;
else return LeafCount(ptr->lchild) + LeafCount(ptr->rchild);
}
/***************************************************/