1. 二叉树的存储结构
1.1 顺序存储结构
顺序存储一棵二叉树时,首先对该树中的每个结点进行编号,然后以各结点的编号为下标,把各结点的值对应存储到一个一位数组中。每个结点的编号与等深度的满二叉树中对应结点的编号相等,即树根结点的编号为1,接着按照从上到下和从左到右的次序,若一个结点的编号为i,则左、右孩子的编号分别为2i和2i+1。如图,各结点上方的数字就是该结点的编号。
假设分别采用一维数组data1和data2来顺序存储上图的两棵二叉树,则两数组中各元素的值如下图所示。
在二叉树的顺序存储结构中,各结点之间的关系是通过下标计算出来的,因此访问每一个结点的双亲和左、右孩子都非常方便。如对于编号为i的结点,其双亲结点的下标为⌊i/2⌋,若存在左孩子,则左孩子结点的下标为2i,若存在右孩子,则右孩子结点的下标为2i+1。
二叉树的顺序存储结构对于存储完全二叉树是合适的,它能够充分利用存储空间,但对于一般二叉树,特别是对于那些单支结点较多的二叉树来说是很不合适的,因为可能只有少数存储位置被利用,而多数或绝大多数的存储位置空间着。
1.2 链接存储结构
在二叉树的链接存储中,通常采用的方法是,在每个结点中设置3个域:值域、左指针域和右指针域。其结点结构为:
链接存储的另一种方法是:在上面的结点结构中再增加一个parent指针域,用来指向其双亲结点。这种存储结构既便于查找孩子结点,也便于查找双亲结点,当然也带来存储空间的相应增加。
同单链表相同,二叉链表既可由独立分配的结点链接而成,也可由数组中的元素结点链接而成。
若采用独立结点,则结点类型可定义为:
1 struct BTreeNode { 2 ElemType data; 3 BTreeNode* left; 4 BTreeNode* right; 5 };
若采用元素结点,则结点类型可定义为:
1 struct ABTreeNode { 2 ElemType data; 3 int left, right; 4 };
元素结点从下标为1的位置起使用,下标为0的位置的左指针域通常用来存储树根指针,右指针域通常用来存储空闲链表的表头指针,空闲链表由空闲结点的right域链接而成。
2. 二叉树遍历
二叉树遍历算法有:前序遍历、中序遍历、后序遍历和按层遍历。
若将跟结点、左子树和右子树分别用D、L和R表述,则前序遍历顺序为DLR,中序遍历顺序为LDR,后序遍历顺序为LRD。
对于上图二叉树,
前序遍历为:ABCDEFG
中序遍历为:CBDAEGF
后序遍历为:CDBGFEA
按层遍历为:ABECDFG
2.1 前序遍历算法
1 void PreOrder(BTreeNode* BT) { 2 if(BT != NULL) { 3 cout << BT->data << ' '; //访问根结点 4 PreOrder(BT->left); //前序遍历左子树 5 PreOrder(BT->right); //前序遍历右子树 6 } 7 }
2.2 中序遍历算法
1 void InOrder(BTreeNode* BT) { 2 if(BT != NULL) { 3 InOrder(BT->left); //中序遍历左子树 4 cout << BT->data << ' '; //访问根结点 5 InOrder(BT->right); //中序遍历右子树 6 } 7 }
2.3 后序遍历算法
1 void PostOrder(BTreeNode* BT) { 2 if(BT != NULL) { 3 PostOrder(BT->left); //后序遍历左子树 4 PostOrder(BT->right); //后序遍历右子树 5 cout << BT->data << ' '; //访问根结点 6 } 7 }