其中data是数据域,存储结点的数据信息。而parent是指针域,存储该结点的双亲在数组中的下标。
/* 树的双亲表示法结点结构定义 */
#define MAX_TREE_SIZE 100
/* 树结点的数据类型,目前暂定为整型 */
typedef int TElemType;
/* 结点结构 */
typedef struct PTNode
{/* 结点数据 */
TElemType data;
/* 双亲位置 */
int parent; }
PTNode;
/* 树结构 */
typedef struct
{/* 结点数组 */
PTNode nodes[MAX_TREE_SIZE];
/* 根的位置和结点数 */
int r, n; }
PTree;
有了这样的结构定义,我们就可以来实现双亲表示法了。由于根结点是没有双亲的,所以我们约定根结点的位置域设置为-1,这也就意味着,我们所有的结点都存有它双亲的位置。
下标 | data | parent |
0 | A | -1 |
1 | B | 0 |
2 | C | 0 |
3 | D | 1 |
4 | E | 2 |
5 | F | 2 |
6 | G | 3 |
7 | H | 3 |
8 | I | 3 |
9 | J | 4 |
这样的存储结构,我们可以根据结点的parent指针很容易找到它的双亲结点,所用的时间复杂度为O(1),直到parent为-1时,表示找到了树结点的根。可如果我们要知道结点的孩子是什么,需要遍历整个结构才行。
改进:我们增加一个结点最左边孩子的域,不妨叫它长子域,这样就可以很容易得到结点的孩子。如果没有孩子的结点,这个长子域就设置为-1。
解释说明:
这个改进的存储结构对于每个节点,除了包含parent指针之外,还增加了一个长子域。长子域存储的是该节点的最左边的子节点的位置信息。
这样做的好处是,我们可以直接通过长子域找到节点的第一个孩子节点,而不需要遍历整个结构。如果节点没有孩子,那么长子域的值设为-1。
在这种改进的存储结构下,我们可以通过以下方式获得节点的双亲和孩子的信息:
- 1. 获得节点的双亲:直接通过节点的parent指针获得,时间复杂度为O(1)。
- 2. 获得节点的第一个孩子:直接通过节点的长子域获得,时间复杂度为O(1)。
- 3. 获得节点的所有孩子:通过遍历节点的所有子节点即可得到,需要遍历整个结构。时间复杂度为O(n),其中n是节点的总数。
这样的改进存储结构可以在常数时间内获得节点的双亲信息,但对于节点的孩子信息仍需要遍历整个结构。
对于有0个或1个孩子结点来说,这样的结构是解决了要找结点孩子的问题了。甚至是有2个孩子,知道了长子是谁,另一个当然就是次子了。
体现兄弟关系:增加一个右兄弟域来体现兄弟关系,也就是说,每一个结点如果它存在右兄弟,则记录下右兄弟的下标。同样的,如果右兄弟不存在,则赋值为-1。
结构扩展可扩展为双亲域、长子域、再有右兄弟域。存储结构的设计是一个非常灵活的过程。一个存储结构设计得是否合理,取决于基于该存储结构的运算是否适合、是否方便,时间复杂度好不好等。注意也不是越多越好,有需要时再设计相应的结构。