通用树(N-array Trees)
通用树是节点的集合,其中每个节点都是一个数据结构,由记录和对其子项的引用列表组成(不允许重复引用)。与链表不同,每个节点存储多个节点的地址。每个节点都存储其子节点的地址,第一个节点的地址将存储在一个名为 root 的单独指针中。
通用树是具有以下属性的 N 叉树:
1. 每个节点都有很多孩子。
2. 每个节点的节点数量事先不知道。
例子:
为了表示上面的树,我们必须考虑最坏的情况,即具有最大子节点的节点(在上面的示例中,6 个子节点)并为每个节点分配那么多指针。
基于这种方法的节点表示可以写为:
C
//Node declaration
struct Node{
int data;
struct Node *firstchild;
struct Node *secondchild;
struct Node *thirdchild;
struct Node *fourthchild;
struct Node *fifthchild;
struct Node *sixthchild;
}
C
//Node declaration
struct Node{
int data;
vector children;
}
C
//Node declaration
struct Node{
int data;
struct Node *firstChild;
struct Node *nextSibling;
}
上述表示的缺点是:
- 内存浪费——并非在所有情况下都需要所有指针。因此,存在大量内存浪费。
- Unknown number of children – 每个节点的子节点数量事先不知道。
简单的方法:
为了将子节点的地址存储在节点中,我们可以使用数组或链表。但是我们将面临他们两个方面的一些问题。
- 在链表中,我们不能随机访问任何孩子的地址。所以会很贵。
- 在数组中,我们可以随机访问任何孩子的地址,但我们只能在其中存储固定数量的孩子地址。
更好的方法:
我们可以使用动态数组来存储孩子的地址。我们可以随机访问任何孩子的地址,并且向量的大小也不是固定的。
C
//Node declaration
struct Node{
int data;
vector children;
}
有效的方法:
第一个孩子/下一个兄弟姐妹表示
在第一个孩子/下一个兄弟表示中,采取的步骤是:
在每个节点上,从左到右链接相同父(兄弟)的孩子。
- 删除从父级到除第一个子级之外的所有子级的链接。
由于我们在孩子之间有一个链接,我们不需要从父母到所有孩子的额外链接。这种表示允许我们从父元素的第一个子元素开始遍历所有元素。
第一个孩子/下一个兄弟表示的节点声明可以写成:
C
//Node declaration
struct Node{
int data;
struct Node *firstChild;
struct Node *nextSibling;
}
优点:
- 内存效率 - 不需要额外的链接,因此节省了大量内存。
- 视为二叉树——由于我们能够将任何通用树转换为二叉树表示,我们可以将所有具有第一个子/下一个兄弟表示的通用树视为二叉树。我们只使用 firstChild 和 nextSibling,而不是左右指针。
- 许多算法可以更容易地表达,因为它只是一棵二叉树。
- 每个节点都是固定大小的,不需要辅助数组或向量。
父数组中通用树的高度
通用树 - 级别顺序遍历