基本上我要做的是从二叉树中删除所有节点,这些节点的值小于通过函数参数传递的值。我写了一个实现,但似乎没有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;
答案 0 :(得分:2)
如果您有二叉搜索树,则当前节点左侧的所有节点的值都较小,而右侧的所有节点都较大。等节点沿着您选择的任何方向前进。对于您的任务,您需要遍历,直到找到您的枢轴,即大于您的值的节点。
对于您找到的每个节点,从顶部开始,小于目标值,您需要:
如果找到的节点大于键值,则从该节点开始向左移动,直到找到值较低的节点。此时,重复上述步骤,直到达到没有较少节点的点。
如果您的树未分类,我不确定您是如何解决问题的。
请注意,您的入口点必须是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;
}