📜  树的左儿童右同级表示(1)

📅  最后修改于: 2023-12-03 15:40:27.280000             🧑  作者: Mango

树的左儿童右同级表示

树的左儿子右兄弟表示法,也称链表表示法,是一种用链表实现树的一种方式。在这种实现方法中,每个节点都包含两个指针:左儿子指针和右兄弟指针。

数据结构

每个节点包含以下元素:

typedef struct TreeNode {
    int data;                // 存储节点的数据
    struct TreeNode *firstChild;   // 左儿子指针
    struct TreeNode *nextSibling;  // 右兄弟指针
} TreeNode, *Tree;

根据定义,每个节点都可以有任意多个子节点,左儿子指针指向第一个子节点,右兄弟指针指向下一个兄弟节点。如果一个节点没有子节点,则其左儿子指针为 NULL;如果一个节点是其父节点的最后一个子节点,则其右兄弟指针为 NULL。

插入节点

插入节点需要分两种情况:

  1. 插入节点作为根节点。
  2. 插入节点作为一个节点的子节点。
void insertNode(Tree *root, int parentValue, int value)
{
    if (*root == NULL) {
        // 根节点为空,插入节点作为根节点
        *root = (TreeNode*) malloc(sizeof(TreeNode));
        (*root)->data = value;
        (*root)->firstChild = NULL;
        (*root)->nextSibling = NULL;
        return;
    }

    TreeNode *parent = findNode(*root, parentValue);
    if (parent == NULL) {
        // 没有找到父节点,插入失败
        return;
    }

    if (parent->firstChild == NULL) {
        // 父节点没有子节点,插入节点作为第一个子节点
        parent->firstChild = (TreeNode*) malloc(sizeof(TreeNode));
        parent->firstChild->data = value;
        parent->firstChild->firstChild = NULL;
        parent->firstChild->nextSibling = NULL;
    } else {
        // 父节点有子节点,找到最后一个子节点的右兄弟,插入节点作为右兄弟
        TreeNode *sibling = parent->firstChild;
        while (sibling->nextSibling != NULL) {
            sibling = sibling->nextSibling;
        }
        sibling->nextSibling = (TreeNode*) malloc(sizeof(TreeNode));
        sibling->nextSibling->data = value;
        sibling->nextSibling->firstChild = NULL;
        sibling->nextSibling->nextSibling = NULL;
    }
}
查找节点

为了插入新节点,我们需要先查找其父节点。遍历树的方法有很多种,这里我们使用深度优先搜索(DFS)。

TreeNode* findNode(TreeNode *node, int value)
{
    if (node == NULL) {
        // 树为空,或者节点不存在,返回 NULL
        return NULL;
    }

    if (node->data == value) {
        // 找到节点,返回
        return node;
    }

    TreeNode *result = findNode(node->firstChild, value);

    if (result == NULL) {
        result = findNode(node->nextSibling, value);
    }

    return result;
}
遍历树

由于树的结构比较复杂,遍历树的方法也比较多。这里我们介绍两种方法:先序遍历和层序遍历。

先序遍历

先序遍历按照“根节点-左子树-右子树”的顺序遍历树。

void preOrderTraversal(TreeNode *node)
{
    if (node == NULL) {
        return;
    }
    printf("%d ", node->data);
    preOrderTraversal(node->firstChild);
    preOrderTraversal(node->nextSibling);
}
层序遍历

层序遍历按照树的层次遍历树。

void levelOrderTraversal(TreeNode *root)
{
    if (root == NULL) {
        return;
    }

    Queue *q = newQueue();
    push(q, root);
    while (!isEmpty(q)) {
        TreeNode *node = (TreeNode*)pop(q);
        printf("%d ", node->data);
        if (node->firstChild != NULL) {
            push(q, node->firstChild);
        }
        if (node->nextSibling != NULL) {
            push(q, node->nextSibling);
        }
    }
}
优缺点

与其他树的表示方式相比,树的左儿子右兄弟表示法具有以下优缺点:

优点
  • 插入和删除节点比较方便,时间复杂度为 O(1)。
  • 不需要存储空间来保存空子节点。
  • 可以表示不完整的树。
缺点
  • 查找节点比较慢,时间复杂度为 O(n)。
  • 不支持树的深度遍历。
总结

树的左儿子右兄弟表示法是一种实现树的方法,它使用链表来存储树。它适用于插入和删除节点比较频繁的场景。该表示法的缺点是查找节点比较慢,不支持深度遍历。