我必须从树中删除一个节点。我首先尝试消除节点根,所以我不必搜索节点,它可以工作。但后来我尝试通过搜索来做,当函数调用自身时,程序在传递第一个if语句后冻结......
问题在于函数void Eliminar(struct arbol *tree, int valor);
:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct arbol
{
int numero;
struct arbol *izq;
struct arbol *der;
};
struct arbol *raiz = NULL;
struct arbol *eliminador = NULL;
int encontrado = 0;
int right = 0, left = 0;
int crear_arbol(int dato);
struct arbol * crear_nodo(int valor);
void ImprimeDNI (struct arbol *tree);
void ImprimeIND (struct arbol *tree);
void ImprimeNDI (struct arbol *tree);
void Buscar (struct arbol *tree, int valor);
void Eliminar (struct arbol *tree,int valor);
int Eliminaroot ();
int Eliminarright(struct arbol *localizador);
int Eliminarleft(struct arbol *localizador);
int main ()
{
int n, i;
char opcion;
int numero;
puts("Ingrese la cantidad de numeros a ingresar");
scanf("%d", &n);
int numeros[n];
puts("Ingrese los numeros separados por espacio o enter");
for(i = 0; i < n; i++)
{
scanf("%d",&numeros[i]);
}
for(i = 0; i < n; i++)
{
crear_arbol(numeros[i]);
}
puts("");
system("pause");
system("cls");
do
{
encontrado = 0;
puts("******** OPCIONES ********");
puts("|B o b| Para buscar un numero");
puts("|E o e| Eliminar un nodo");
puts("|I o i| Imprimir de las 3 formas principales");
fflush(stdin);
opcion = getch();
switch(opcion)
{
case 'B': case 'b': puts("Ingrese el numero a buscar"); scanf("%d",&numero); Buscar(raiz,numero);
if(encontrado == 0) {puts("El numero no esta en el arbol");} break;
case 'E': case 'e': puts("Ingrese el numero a eliminar"); scanf("%d", &numero);
if(raiz->numero == numero)
{
Eliminaroot();
}
else
{
Eliminar(raiz,numero);
if(right == 0 && left == 0)
{
puts("No se encontro el numero");
}
if(right == 1)
{
Eliminarright(eliminador);
}
if(left == 1)
{
Eliminarleft(eliminador);
}
}
break;
case 'I': case 'i': ImprimeDNI(raiz); puts(""); ImprimeIND(raiz); puts(""); ImprimeNDI(raiz); puts(""); break;
default: puts("Opcion Invalida"); break;
}
puts("");
system("pause");
system("cls");
}while (opcion != 'T' || opcion != 't');
return 0;
}
int crear_arbol(int dato)
{
struct arbol *recorrer = raiz;
struct arbol *nuevo;
if(raiz == NULL)
{
raiz = crear_nodo(dato);
return 1;
}
else
{
nuevo = crear_nodo(dato);
}
while (1) {
if(recorrer->numero <= nuevo->numero)
{
if(recorrer->der == NULL)//si las ramas de donde esta el puntero que recorre son NULL, significa
{ //que es la ultima comparacion
recorrer->der = nuevo;
break;
}
recorrer = recorrer->der;
}
else
{
if(recorrer->izq == NULL)//lo mismo que el if de arriba
{
recorrer->izq = nuevo;
break;
}
recorrer = recorrer->izq;
}
}//while
return 1;
}
struct arbol * crear_nodo(int valor)
{
struct arbol *aux;
aux = (struct arbol*)malloc(sizeof(struct arbol));
aux->numero = valor;
aux->izq = NULL;
aux->der = NULL;
return aux;
}
void ImprimeDNI (struct arbol *tree)
{
if(!tree)
return;
ImprimeDNI(tree->der);
printf("%d, ", tree->numero);
ImprimeDNI(tree->izq);
}
void ImprimeIND (struct arbol *tree)
{
if(!tree)
return;
ImprimeIND(tree->izq);
printf("%d, ", tree->numero);
ImprimeIND(tree->der);
}
void ImprimeNDI (struct arbol *tree)
{
if(!tree)
return;
printf("%d, ", tree->numero);
ImprimeNDI(tree->der);
ImprimeNDI(tree->izq);
}
void Buscar (struct arbol *tree, int valor)
{
if(tree->numero == valor)
{printf("El numero si se encuentra en el arbol"); encontrado = 1;}
if(!tree)
return;
Buscar(tree->der, valor);
Buscar(tree->izq,valor);
}
int Eliminaroot ()
{
int encontrado = 0;
struct arbol *aux = raiz;
struct arbol *buscador = raiz->der;
for(; buscador->der != NULL ; buscador = buscador->der)
{
if(buscador->izq != NULL)
{
encontrado = 1;
for(; buscador->izq->izq != NULL ; buscador = buscador->izq)
{
}
break;
}//if
}
if(encontrado == 0)
{
if(raiz->der == NULL)
{
raiz = aux->izq;
raiz->izq = aux->izq->izq;
raiz->der = aux->izq->der;
}
else
{
raiz = aux->der;
raiz->izq = aux->izq;
raiz->der = aux->der->der;
free(aux);
}
}
else
{
raiz = buscador->izq;
raiz->der = aux->der;
raiz->izq = aux->izq;
buscador->izq = NULL;
free(aux);
}
return 1;
}
void Eliminar (struct arbol *tree, int valor)
{
if(tree->izq->numero == valor)
{
eliminador = tree;
left = 1;
}
puts("AAAA");
if(tree->der->numero == valor)
{
eliminador = tree;
right = 1;
}
if(!tree)
return;
Eliminar(tree->der, valor);
Eliminar(tree->izq, valor);
}
int Eliminarright(struct arbol *localizador)
{
return 1;
}
int Eliminarleft(struct arbol *localizador)
{
return 1;
}*
答案 0 :(得分:1)
正如尼克建议的那样,您应该检查tree
开头Eliminar
是否有效。但是,如果第一个if
语句执行正常,则tree
不能为NULL
。但是,tree->der
可以在解除引用之前检查它。当然,第一个if中tree->izq
的相同 - 只是因为它在你第一次调用这个函数时不是NULL,所以不要假设它永远不会。
进一步说明:您正在valor
中搜索具有值Eliminar
的节点(因此这是一个错误的名称 - 您没有消除那里的节点,只是将其标记为以后去除)。
如果您找到它,则无需继续搜索,因此您可以return
立即离开if
个分支。
此外,当您在左侧或右侧子树中找到valor
时,您可以设置left
或right
标记,并调用Eliminarleft
或{ {1}}因此。直接存储要删除的左子树或右子树会更简单,因此您可以删除两个标志和两个删除方法:
Eliminarright
这更清洁,但我们可以走得更远。请注意,您正在检查void Eliminar (struct arbol *tree, int valor)
{
if(!tree)
return;
if(tree->izq && tree->izq->numero == valor)
{
eliminador = tree->izq;
return;
}
puts("AAAA");
if(tree->der && tree->der->numero == valor)
{
eliminador = tree->der;
return;
}
Eliminar(tree->der, valor);
Eliminar(tree->izq, valor);
}
...
Eliminar(raiz,numero);
if(!eliminador)
{
puts("No se encontro el numero");
}
else
{
Eliminar(eliminador);
}
中的左右子树,然后再进行相同的递归。相反,只需检查Eliminar
本身,然后递归:
tree
答案 1 :(得分:0)
您没有检查函数顶部的tree
指针,因此可能导致对空指针的访问冲突。将if (!tree) return;
检查移至功能顶部。
答案 2 :(得分:0)
@PéterTörök的回答让你在那里的一部分。在我看来,你有一个标准的二叉树设置,左边是“值小于”,右边是“值大于”(或者如果你允许重复,可能是&gt; =。
除去全局变量(Eliminar
设置eliminador
以及左/右标志),这是非常好的,你可以使用指针到指针来完成。它不是让Eliminar
获取树节点,而是可以获取指向树节点的指针,并在删除节点时更新它。此外,删除节点后,您可以停止:
int Eliminar(struct arbol **tree_p, int valor)
{
struct arbol *tree = *tree_p;
if (!tree)
return 0; /* nothing to remove */
if (tree->numero == valor) {
/* this is the node to remove */
*tree_p = rewrite(tree); /* rewrite subtree from here down, and update */
return 1; /* indicate that we're done */
}
/* didn't find the node to remove ... use left or right subtree for next attempt */
tree_p = tree->numero > valor ? &tree->der : &tree->izq;
return Eliminar(tree_p, valor);
}
(不确定我上面的左/右是否正确;我留给你工作:-))。
现在很容易将递归转换为迭代。困难的部分是rewrite()
,因为您可以拥有tree
的左右子树。如果你只有一个,这很容易,但如果你有两个,那就不再容易了。我再次将其作为练习......: - )
您可以Eliminar
返回实际删除的树节点(如果树中没有NULL
,则返回valor
);这在某些情况下很有用。在任何一种情况下,您只需执行:result = Eliminar(&root, valor);
更新根节点并获取成功/失败指示符。