从树中消除节点功能在C中不起作用

时间:2012-04-06 07:35:08

标签: c

我必须从树中删除一个节点。我首先尝试消除节点根,所以我不必搜索节点,它可以工作。但后来我尝试通过搜索来做,当函数调用自身时,程序在传递第一个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;
}*

3 个答案:

答案 0 :(得分:1)

正如尼克建议的那样,您应该检查tree开头Eliminar是否有效。但是,如果第一个if语句执行正常,则tree不能为NULL。但是,tree->der可以在解除引用之前检查它。当然,第一个if中tree->izq的相同 - 只是因为它在你第一次调用这个函数时不是NULL,所以不要假设它永远不会。

进一步说明:您正在valor中搜索具有值Eliminar的节点(因此这是一个错误的名称 - 您没有消除那里的节点,只是将其标记为以后去除)。

如果您找到它,则无需继续搜索,因此您可以return立即离开if个分支。

此外,当您在左侧或右侧子树中找到valor时,您可以设置leftright标记,并调用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);更新根节点并获取成功/失败指示符。