参考问题Deallocating binary-tree structure in C
struct Node{
Node *parent;
Node *next;
Node *child;
}
我试图释放二叉树。我遇到的问题是分配的对象是5520,并且对自由函数的调用次数是2747.我不知道为什么,它应该真正自由并遍历树中的所有节点,这里是我使用的代码
int number_of_iterations =0;
int number_of_deletions =0;
void removetree(Node *node)
{
number_of_iterations++;
while(node != NULL)
{
Node *temp = node;
if(node->child != NULL)
{
node = node->child;
temp->child = node->next;
node->next = temp;
}
else
{
node = node->next;
remove(temp);
number_of_deletions++
}
}
}
迭代次数为5440,删除次数为2747。
新的固定代码:代码是否正确?
const Node *next(const Node *node)
{
if (node == NULL) return NULL;
if (node->child) return node->child;
while (node && node->next == NULL) {
node = node->parent;
}
if (node) return node->next;
return NULL;
}
for ( p= ctx->obj_root; p; p = next(p)) {
free(p);
}
答案 0 :(得分:4)
如果你的节点确实包含有效的parent
指针,那么整个事情可以用更紧凑和可读的方式完成
void removetree(Node *node)
{
while (node != NULL)
{
Node *next = node->child;
/* If child subtree exists, we have to delete that child subtree
first. Once the child subtree is gone, we'll be able to delete
this node. At this moment, if child subtree exists, don't delete
anything yet - just descend into the child subtree */
node->child = NULL;
/* Setting child pointer to null at this early stage ensures that
when we emerge from child subtree back to this node again, we will
be aware of the fact that child subtree is gone */
if (next == NULL)
{ /* Child subtree does not exist - delete the current node,
and proceed to sibling node. If no sibling, the current
subtree is fully deleted - ascend to parent */
next = node->next != NULL ? node->next : node->parent;
remove(node); // or `free(node)`
}
node = next;
}
}
答案 1 :(得分:3)
这个 是二叉树!由于您选择的名称,人们会感到困惑,next
在链接列表中很常见,但类型是重要的,您有一个节点引用两个相同的节点类型,这一切都很重要。
从此处获取:您链接到的https://codegolf.stackexchange.com/a/489/15982。我将left
重命名为child
,将right
重命名为next
:)
void removetree(Node *root) {
struct Node * node = root;
struct Node * up = NULL;
while (node != NULL) {
if (node->child != NULL) {
struct Node * child = node->child;
node->child = up;
up = node;
node = child;
} else if (node->next != NULL) {
struct Node * next = node->next;
node->child = up;
node->next = NULL;
up = node;
node = next;
} else {
if (up == NULL) {
free(node);
node = NULL;
}
while (up != NULL) {
free(node);
if (up->next != NULL) {
node = up->next;
up->next = NULL;
break;
} else {
node = up;
up = up->child;
}
}
}
}
}
答案 2 :(得分:1)
首先要说的是,如果你试图以非递归的方式解决递归问题,你就会遇到麻烦。
我看到错误的答案被选为好的答案,所以我会尝试展示我的方法:
我开始使用指针引用而不是普通指针,因为传递根指针引用可以更容易地检测(和更新)指向根节点的指针。因此,例程的界面将是:
void delete_tree(struct node * * const ref);
它表示对指向根节点的指针的引用。我将下降到节点,如果child
或next
中的一个是NULL
,那么只需将引用的指针指向另一个链接就可以自由地消除此节点(所以我不会失去它)。如果节点有两个子节点(child
和next
都是!= NULL
),那么在其中一个分支折叠之前我无法删除此节点,然后我选择其中一个分支并移动引用(我宣布ref
参数const
以确保我不会修改它,所以我使用另一个移动参考文件)
struct node **moving_reference;
然后,算法如下:
void tree_delete(struct node ** const static_ref)
{
while (*static_ref) {
struct node **moving_ref = static_ref;
while (*moving_ref) {
struct node *to_be_deleted = NULL;
if ((*moving_ref)->child && (*moving_ref)->next) {
/* we have both children, we cannot delete until
* ulterior pass. Just move the reference. */
moving_ref = &(*moving_ref)->child;
} else if ((*moving_ref)->child) {
/* not both != NULL and child != NULL,
* so next == NULL */
to_be_deleted = *moving_ref;
*moving_ref = to_be_deleted->child;
} else {
/* not both != NULL and child == NULL,
* so follow next */
to_be_deleted = *moving_ref;
*moving_ref = to_be_deleted->next;
} /* if, else if */
/* now, delete the unlinked node, if available */
if (to_be_deleted) node_delete(to_be_deleted);
} /* while (*moving_ref) */
} /* while (*static_ref) */
} /* delete_tree */
我已将此算法包含在一个完整的示例中,向您显示部分树和moving_ref
在树中移动时的位置。它还显示了删除它所需的传递。
#include <stdio.h>
#include <stdlib.h>
#define N 100
#define D(x) __FILE__":%d:%s: " x, __LINE__, __func__
#define ASSERT(x) do { \
int res; \
printf(D("ASSERT: (" #x ") ==> %s\n"), \
(res = (int)(x)) ? "TRUE" : "FALSE"); \
if (!res) exit(EXIT_FAILURE); \
} while (0)
struct node {
int key;
struct node *child;
struct node *next;
}; /* struct node */
struct node *node_alloc(void);
void node_delete(struct node *n);
/* This routine has been written recursively to show the simplicity
* of traversing the tree when you can use recursive algorithms. */
void tree_traverse(struct node *n, struct node *p, int lvl)
{
while(n) {
printf(D("%*s[%d]\n"), lvl<<2, p && p == n ? ">" : "", n->key);
tree_traverse(n->child, p, lvl+1);
n = n->next;
} /* while */
} /* tree_traverse */
void tree_delete(struct node ** const static_ref)
{
int pass;
printf(D("BEGIN\n"));
for (pass = 1; *static_ref; pass++) {
struct node **moving_ref = static_ref;
printf(D("Pass #%d: Considering node %d:\n"),
pass, (*moving_ref)->key);
while (*moving_ref) {
struct node *to_be_deleted = NULL;
/* print the tree before deciding what to do. */
tree_traverse(*static_ref, *moving_ref, 0);
if ((*moving_ref)->child && (*moving_ref)->next) {
printf(D("Cannot remove, Node [%d] has both children, "
"skip to 'child'\n"),
(*moving_ref)->key);
/* we have both children, we cannot delete until
* ulterior pass. Just move the reference. */
moving_ref = &(*moving_ref)->child;
} else if ((*moving_ref)->child) {
/* not both != NULL and child != NULL,
* so next == NULL */
to_be_deleted = *moving_ref;
printf(D("Deleting [%d], link through 'child' pointer\n"),
to_be_deleted->key);
*moving_ref = to_be_deleted->child;
} else {
/* not both != NULL and child != NULL,
* so follow next */
to_be_deleted = *moving_ref;
printf(D("Deleting [%d], link through 'next' pointer\n"),
to_be_deleted->key);
*moving_ref = to_be_deleted->next;
} /* if, else if */
/* now, delete the unlinked node, if available */
if (to_be_deleted) node_delete(to_be_deleted);
} /* while (*moving_ref) */
printf(D("Pass #%d end.\n"), pass);
} /* for */
printf(D("END.\n"));
} /* delete_tree */
struct node heap[N] = {0};
size_t allocated = 0;
size_t deleted = 0;
/* simple allocation/free routines, normally use malloc(3). */
struct node *node_alloc()
{
return heap + allocated++;
} /* node_alloc */
void node_delete(struct node *n)
{
if (n->key == -1) {
fprintf(stderr,
D("doubly freed node %ld\n"),
(n - heap));
}
n->key = -1;
n->child = n->next = NULL;
deleted++;
} /* node_delete */
/* main simulation program. */
int main()
{
size_t i;
printf(D("Allocating %d nodes...\n"), N);
for (i = 0; i < N; i++) {
struct node *n;
n = node_alloc(); /* the node */
n->key = i;
n->next = NULL;
n->child = NULL;
printf(D("Node %d"), n->key);
if (i) { /* when we have more than one node */
/* get a parent for it. */
struct node *p = heap + (rand() % i);
printf(", parent %d", p->key);
/* insert as a child of the parent */
n->next = p->child;
p->child = n;
} /* if */
printf("\n");
} /* for */
struct node *root = heap;
ASSERT(allocated == N);
ASSERT(deleted == 0);
printf(D("Complete tree:\n"));
tree_traverse(root, NULL, 0);
tree_delete(&root);
ASSERT(allocated == N);
ASSERT(deleted == N);
} /* main */
答案 3 :(得分:0)
我愿意
void removeTree(Node *node){
Node *temp;
while (node !=NULL){
if (node->child != NULL) {
removeTree(node->child);
}
temp = node;
node = node->next;
free(temp);
}
}
非递归版:
void removeTree(Node *node){
Node *temp;
while (node !=NULL){
temp = node;
while(temp != node) {
for(;temp->child == NULL; temp = temp->child); //Go to the leaf
if (temp->next == NULL)
free(temp); //free the leaf
else { // If there is a next move it to the child an repeat
temp->child = temp->next;
temp->next = temp->next->next;
}
}
node = temp->next; // Move to the next
free(temp)
}
}
我认为这应该有用
答案 4 :(得分:0)
为什么不这样做呢?
void removetree(Node *node) {
if (node == NULL) {
return;
}
number_of_iterations++;
removetree(node->next);
removetree(node->child);
free(node);
number_of_deletions++;
}
答案 5 :(得分:0)
该实现不适用于具有根f
的树,该树只有一个子1
。
2
执行NodeOne.parent = null
NodeOne.next = null
NodeOne.child = NodeTwo
NodeTwo.parent = null
NodeTwo.next = null
NodeTwo.child = null
不会在Removetree(NodeOne)
上致电remove
。
答案 6 :(得分:0)
您只释放子树,而不释放父节点。假设child
代表节点的left
子节点,next
代表right
子节点。然后,您的代码变为:
struct Node{
Node *parent;
Node *right;
Node *left;
}
int number_of_iterations =0;
int number_of_deletions =0;
void removetree(Node *node)
{
number_of_iterations++;
while(node != NULL)
{
Node *temp = node;
if(node->left != NULL)
{
node = node->left;
temp->left = node->right;
node->right = temp;
}
else
{
node = node->right;
remove(temp);
number_of_deletions++
}
}
}
每次while循环完成一次迭代时,左侧子节点将没有指向它的指针。 以,例如以下树:
1
2 3
4 5 6 7
当node
是根节点时,会发生以下情况
node
指向'1'temp
指向'1'node
现在指向'2'temp->left
现在指向'5'node->right
现在指向'1'在下一次迭代中,您将开始
node
也指向'2'和temp
。如您所见,您没有删除节点“1”。这将重复。
为了解决这个问题,如果您想避免递归,可能需要考虑使用BFS和类似队列的结构来删除所有节点。
修改强> BFS方式:
Q
node
添加到Q
node
的左侧节点添加到Q
node
的正确节点添加到Q
node
并将其从Q
node
= Q
Q
变空这使用了队列是FIFO结构的事实。
答案 7 :(得分:0)
@AnT提出的代码是不正确的,因为它在遍历和释放其左子树之后但在使用正确的子树执行相同之前释放节点。因此,在从右子树升序时,我们将踩到已经释放的父节点。
这是一种正确的方法,在C89中。但它仍然需要parent
个链接。
void igena_avl_subtree_free( igena_avl_node_p root ) {
igena_avl_node_p next_node;
{
while (root != NULL) {
if (root->left != NULL) {
next_node = root->left;
root->left = NULL;
} else if (root->right != NULL) {
next_node = root->right;
root->right = NULL;
} else {
next_node = root->parent;
free( root );
}
root = next_node;
}
}}
直接从我自己的项目Gena中获取。