从C中删除小于/大于给定值的二叉树中的所有值?

时间:2017-01-11 21:28:12

标签: c binary-search-tree nodes free

基本上我要做的是从二叉树中删除所有节点,这些节点的值小于通过函数参数传递的值。我写了一个实现,但似乎没有100%正确的工作。它确实删除了一些价值较低的节点,但不是全部。任何输入都非常感谢。提前感谢愿意提供帮助的任何人。 : - )

该功能的代码如下:

NODE* removeBelowGivenValue(NODE** root, unsigned key)
{
    if (*root == NULL)
        return *root;
    (*root)->left = removeBelowGivenValue(&(*root)->left, key);
    (*root)->right = removeBelowGivenValue(&(*root)->right, key);
    if ((*root)->data.numOfPoints < key)
    {
        NODE* rightChild = (*root)->right;
        free(*root);
        *root=NULL;
        return rightChild;
    }
    return *root;
}

typedef struct
{
    char index[12];
    char firstName[21], lastName[21];
    unsigned numOfPoints;
}STUDENT;

typedef struct node
{
    STUDENT data;
    struct node* left, *right;
}NODE;

2 个答案:

答案 0 :(得分:2)

如果您有二叉搜索树,则当前节点左侧的所有节点的值都较小,而右侧的所有节点都较大。等节点沿着您选择的任何方向前进。对于您的任务,您需要遍历,直到找到您的枢轴,即大于您的值的节点。

对于您找到的每个节点,从顶部开始,小于目标值,您需要:

  1. 保留正确的子节点。
  2. 遍历左侧树,删除每个节点。
  3. 删除开始此过程的顶级节点。
  4. 移至剩余的右侧节点并重复。
  5. 如果找到的节点大于键值,则从该节点开始向左移动,直到找到值较低的节点。此时,重复上述步骤,直到达到没有较少节点的点。

    如果您的树未分类,我不确定您是如何解决问题的。

    请注意,您的入口点必须是NODE **而不是NODE *,因为您可能需要删除根节点并将指针更改为新根。

答案 1 :(得分:1)

这是一种从树中删除节点的非常通用的方法。下面的方法假设您要保留按顺序遍历顺序,这意味着所有其他节点(除了已移除的节点)在删除之后仍将以相同的相对顺序显示按顺序遍历二叉树的遍历。

首先,函数签名将删除节点,并将其父节点作为参数。让我们确定前提条件。

NODE *
removeNode (NODE *node, NODE *parent)
{
    NODE **from;
    assert(parent);
    assert(parent->left == node || parent->right == node);
    from = (parent->left == node) ? &parent->left : &parent->right;
    /*...*/
}

首先,简单的情况是节点最多只有一个孩子。然后,父级采用该子级,并且可以删除该节点。

    if (node->left == NULL || node->right == NULL) {
        *from = node->left ? node->left : node->right;
        node->left = node->right = NULL;
        return node; /* caller frees the node */
    }

否则,节点是一个完全内部节点,需要保留其子树。实现此目的的一种方法是将左子树附加到右子树的左侧。为此,我们需要找到右子树的最左边节点。

    NODE *leftmost = findLeftmostNode(node->right);
    assert(leftmost->left == NULL);
    leftmost->left = node->left;
    *from = node->right;
    node->left = node->right = NULL;
    return node; /* caller frees the node */

而且,findLeftmost()相当简单。它沿着子树的左侧跟随,直到它到达终点。

NODE *
findLeftmostNode (NODE *node)
{
    assert(node);
    while (node->left) {
        node = node->left;
    }
    return node;
}

现在,了解removeNode()逻辑,可以考虑removeBelowGivenValue()。由于没有明确标准值与二叉树排序有关,因此一般的解决方案是访问每个节点并查看是否满足某些删除标准。

为了实现这一点,我们实现了辅助函数,它修剪满足条件的树上的所有节点。辅助函数假定父级已被取消资格进行修剪,因此它只关注修剪父级下的子树。

typedef struct prune_node_condition {
    bool (*test)(struct prune_node_condition *, NODE *);
} PRUNE_NODE_COND;

void pruneOneSide (NODE **, NODE *, PRUNE_NODE_COND *);

void
pruneBothSides (NODE *parent, PRUNE_NODE_COND *condition)
{
    /* parent's removal already ruled out */
    if (parent == NULL) return;

    pruneOneSide(&parent->left, parent, condition);
    pruneOneSide(&parent->right, parent, condition);
}

我们需要一个循环来执行修剪,因为每个被删除的节点实际上可能会更改父指向的节点,并且需要再次针对该条件测试更改的节点。

void
pruneOneSide (NODE **side, NODE *parent, PRUNE_NODE_COND *condition)
{
    while (*side && condition->test(condition, *side)) {
        free(removeNode(*side, parent));
    }
    pruneBothSides(*side, condition);
}

由于删除节点可能会影响根本身,因此修剪帮助程序的包装器将返回新根。为了获得新的根,它会创建一个虚拟父级来保存根,然后修剪单个虚拟端。

NODE *
removeNodesIf (NODE *root, PRUNE_NODE_COND *condition)
{
    NODE dummy;
    dummy.left = root;
    pruneOneSide(&dummy.left, &dummy, condition);
    return dummy.left;
}

最后,我们可以实现原来要求的功能。 removeBelowGivenValue需要少于检查条件。

struct prune_with_key_condition {
    PRUNE_NODE_COND base;
    unsigned key;
};

bool
remove_below_given_value_test (PRUNE_NODE_COND *base, NODE *node)
{
    struct prune_with_key_condition *s = (void *)base;
    return node->data.numOfPoints < s->key;
}

现在,可以通过将此条件传递给removeNodesIf()函数来实现实际函数。

NODE *
removeBelowGivenValue(NODE** root, unsigned key)
{
    struct prune_with_key_condition c = {
        { remove_below_given_value_test },
        key
    };

    if (root == NULL || *root == NULL) {
        return NULL;
    }

    *root = removeNodesIf(*root, &c.base);
    return *root;
}