Eliminar datos de lista enlazada

Iniciado por erickgracia, 15 Abril 2014, 00:19 AM

0 Miembros y 1 Visitante están viendo este tema.

erickgracia

Hola, soy nuevo en este foro , tengo un problema al intentar hacer un codigo que borre los datos de una lista enlazada:

lo que intente fue que al declarar apuntadores de los nodos llamados anterior, actual y siguiente, dados que apuntaran a 3 nodos diferentes de la lista mientras se va recorriendo dentro del    "while(recorre != NULL){}"   y que si encontraba el valor de llave , el apuntador del nodo anterior , apunte a "siguiente" , y y "eliminar" el nodo "actual" mediante el comando free();


El problema es que al ejecutar el programa entero y entra en la funcion de eliminarLista();  se detiene el programa despues de pedir el valor del entero a eliminar (o tal vez esta teniendo un loop el ciclo, la verdad no estoy seguro) estaria muy agradecido si me ayudaran con este problema, ya que es una base para un trabajo de la universidad del cual me basarè.


he aqui el codigo, o por lo menos la parte que de eliminar datos, tambien pongo la estructura con la que anda trabajando el codigo.



typedef struct nodo{
int llave;
struct nodo * sig; /* auto referencia */
}nodoLista;



void eliminarLista(){
nodoLista* anterior;
nodoLista* actual;
nodoLista* siguiente;
nodoLista* recorre=primero;

int op;
printf("Cual es el entero a eliminar?");
scanf("%d", &op);

anterior=primero;
actual=primero;
siguiente=primero->sig;

while(recorre != NULL){

if(recorre->llave == op){
anterior->sig=siguiente;
free(actual);
}
else{}
anterior=recorre;
recorre = recorre->sig;
actual=recorre;
if(recorre != NULL){siguiente = recorre->sig;}
}
}













y por si acaso, aqui va el codigo entero(tomen en cuenta que es un archivo sencillo para practicar  ;D ) :

#include <stdio.h>
#include <stdlib.h>

#define FALSE 0
#define TRUE 1

typedef int boolean;

boolean estaVacia();

typedef struct nodo{
int llave;
struct nodo * sig; /* auto referencia */
}nodoLista;

nodoLista * primero = NULL;
nodoLista * ultimo = NULL;

void insertarInicio();
void insertarFinal();
void mostrarLista();
void eliminarLista();

int main(void) {
setvbuf(stdout, NULL, _IONBF, 0);
int opcion;
do{
printf("\n\nMENU");
printf("\n0.- Salir");
printf("\n1.- Agregar un nodo al inicio");
printf("\n2.- Mostrar los valores de la lista");
printf("\n3.- Agregar un nodo al final");
printf("\n4.- Eliminar un elemento");

printf("\n\nDame tu opci�n");
scanf("%d", &opcion);

switch(opcion){
case 0:
break;
case 1:
insertarInicio();
break;
case 2:
mostrarLista();
break;
case 3:
insertarFinal();
break;
case 4:
eliminarLista();
break;
}

} while (opcion != 0);


return EXIT_SUCCESS;
}

void insertarInicio(){
nodoLista * nuevo;
nuevo = (nodoLista *)malloc(sizeof(nodoLista));

printf("\n\nDame el valor de la llave");
scanf("%d", &nuevo->llave);
nuevo->sig = primero;
primero = nuevo;

if(primero->sig == NULL)
ultimo = primero;
}

void insertarFinal(){
if (estaVacia()){
insertarInicio();
return;
}

nodoLista * nuevo;
nuevo = (nodoLista *)malloc(sizeof(nodoLista));

printf("\n\nDame el valor de la llave");
scanf("%d", &nuevo->llave);
nuevo->sig = NULL;
ultimo->sig = nuevo;
ultimo = nuevo;
}

void mostrarLista(){
if (estaVacia()){
printf("\n\nLa lista est� vac�a");
return;
}
nodoLista * recorre = primero;
while (recorre != NULL){
printf("%d  ", recorre->llave);
recorre = recorre->sig;
}
}

void eliminarLista(){
nodoLista* anterior;
nodoLista* actual;
nodoLista* siguiente;
nodoLista* recorre=primero;

int op;
printf("Cual es el entero a eliminar?");
scanf("%d", &op);

anterior=primero;
actual=primero;
siguiente=primero->sig;

while(recorre != NULL){

if(recorre->llave == op){
anterior->sig=siguiente;
free(actual);
}
else{}
anterior=recorre;
recorre = recorre->sig;
actual=recorre;
if(recorre != NULL){siguiente = recorre->sig;}
}
}

boolean estaVacia(){
if (primero == NULL)
return TRUE;
else
return FALSE;
}


eferion


Cita de: erickgracia en 15 Abril 2014, 00:19 AM
El problema es que al ejecutar el programa entero y entra en la funcion de eliminarLista();  se detiene el programa despues de pedir el valor del entero a eliminar (o tal vez esta teniendo un loop el ciclo, la verdad no estoy seguro)

Saber lo que está pasando es tan sencillo como usar un depurador... que no te den miedo... te vas a hartar a usarlos si en algún momento te vas a dedicar a programar. Es una buena idea familiarizarse con su uso.

Para eliminar un elemento de una lista enlazada únicamente necesitas dos punteros... "anterior" y "actual":

En tu caso, el problema es que al eliminar un nodo cualquiera desplazas "anterior". Si tu eliminas un nodo no puedes actualizar "anterior". Puedes comprobarlo si dibujas el proceso sobre un papel.


void eliminarLista()
{
  nodoLista* anterior = NULL;
  nodoLista* actual = primero;

  int op;
  printf("Cual es el entero a eliminar?");
  scanf("%d", &op );

  while( actual != NULL )
  {
    if( actual->llave == op )
    {
      // Si eliminamos el primer elemento de la lista hay que operar de forma diferente
      if ( anterior == NULL )
        primero = actual->sig;
      else
        anterior->sig = actual->sig;

      free( actual );
      actual = anterior->sig;
      break; // Este break solo tiene sentido si la lista no admite duplicados
    }
    else
    {
      anterior = actual;
      actual = actual->sig;
    }
  }
}


En tu caso, "actual" y "recorre" apuntan al mismo sitio... no tiene sentido porque te obliga a tener sincronizados dos punteros sin ninguna necesidad.

Además:

Código (cpp) [Seleccionar]
if ( recorre != NULL){siguiente = recorre->sig;

esto para que?? la siguiente línea que se ejecuta es la condición del while, que dice que si recorre es NULL sales del while.

Además, no entiendo por qué razón guardas el puntero "ultimo". En una lista enlazada, el "sig" del último elemento apunta a NULL, luego saber si has llegado al final de la lista es tan sencillo como comprobar el "sig" de cada elemento.

También, mi consejo, es que tiendas a reutilizar código... no tiene sentido que tengas dos funciones para añadir elementos a la lista ( una para añadir al principio y otra para añadir al final ). Tiene más sentido que haya una función única y que sea ésta la que sepa añadir un elemento en cualquier posición... así cuando uses tu librería no tendrás que preocuparte por la posición concreta del elemento a insertar.

También te recomiendo no usar variables globales. Es mejor pasar estas variables como argumentos de las funciones.

Un saludo.

nolasco281

#2
Hola

veo que mencionas depuradores quisiera saber a que se refiere y si me puedes dar algun ejemplo, ya que tambien lo he leido en otras partes pero pensaba que esto lo realizaba el IDE al momenton de compilar el programa.

Encuanto al programa

realize las modificaciones que apunta eferion y el tiempo de ejecucion se redujo notablemente
por lo menos en mi caso de 3.61 a el rango de 0.50

ha gracias por la explicacion la entendi muy bien : ).

aca la prueba


Saludos y gracias.
Lo que se puede imaginar... se puede programar.

eferion

Cita de: nolasco281 en 15 Abril 2014, 16:50 PM
Hola

veo que mencionas depuradores quisiera saber a que se refiere y si me puedes dar algun ejemplo, ya que tambien lo he leido en otras partes pero pensaba que esto lo realizaba el IDE al mometon de compilar el programa.

Saludos.

Un depurador es un programa que, dado un programa compilado en modo debug y su correspondiente código fuente es capaz de presentar al usuario una interfaz que le permite seguir la traza de ejecución del código.

Los IDEs no son más que "fachadas" con un catálogo de utilidades que facilita el proceso de programación... pero lo dicho, no son más que fachadas. Al igual que un IDE se vale de un compilador independiente para compilar los programas, para depurar se vale de un depurador.

¿Se puede depurar una aplicación que no está compilada en modo debug? SI, solo que debido a las optimizaciones que hace el compilador y a la inexistencia de información de depuración, tienes que bajar forzosamente a nivel ensamblador... esto se usa, por ejemplo, en ingeniería inversa.

Depuradores los hay a patadas... cada uno con sus características y sus limitaciones. Puedes encontrar mucha información al respecto navegando por internet.


nolasco281

Gracias se entendio perfectamente.

Cita de: eferion en 15 Abril 2014, 17:02 PM
Puedes encontrar mucha información al respecto navegando por internet.

solo que no sabia pensaba, que la depuracion se realizaba en la compilacion por eso no me habia dado a la tarea de investigar.

pero muchas gracias por la aclaracion.

Saludos y muchas gracias.
Lo que se puede imaginar... se puede programar.

Eternal Idol

Los depuradores trabajan con o sin el codigo fuente, obviamente es mucho mas comodo hacerlo con el codigo y si generas la informacion de depuracion, como los simbolos PDB de Microsoft, tambien podes depurar con codigo en modo release. Igual tarde o temprano vas a terminar depurando en ensamblador si te dedicas a esto profesionalmente ...
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

erickgracia

Cita de: eferion en 15 Abril 2014, 09:55 AM
Saber lo que está pasando es tan sencillo como usar un depurador... que no te den miedo... te vas a hartar a usarlos si en algún momento te vas a dedicar a programar. Es una buena idea familiarizarse con su uso.

Para eliminar un elemento de una lista enlazada únicamente necesitas dos punteros... "anterior" y "actual":

En tu caso, el problema es que al eliminar un nodo cualquiera desplazas "anterior". Si tu eliminas un nodo no puedes actualizar "anterior". Puedes comprobarlo si dibujas el proceso sobre un papel.


void eliminarLista()
{
  nodoLista* anterior = NULL;
  nodoLista* actual = primero;

  int op;
  printf("Cual es el entero a eliminar?");
  scanf("%d", &op );

  while( actual != NULL )
  {
    if( actual->llave == op )
    {
      // Si eliminamos el primer elemento de la lista hay que operar de forma diferente
      if ( anterior == NULL )
        primero = actual->sig;
      else
        anterior->sig = actual->sig;

      free( actual );
      actual = anterior->sig;
      break; // Este break solo tiene sentido si la lista no admite duplicados
    }
    else
    {
      anterior = actual;
      actual = actual->sig;
    }
  }
}


En tu caso, "actual" y "recorre" apuntan al mismo sitio... no tiene sentido porque te obliga a tener sincronizados dos punteros sin ninguna necesidad.

Además:

Código (cpp) [Seleccionar]
if ( recorre != NULL){siguiente = recorre->sig;

esto para que?? la siguiente línea que se ejecuta es la condición del while, que dice que si recorre es NULL sales del while.

Además, no entiendo por qué razón guardas el puntero "ultimo". En una lista enlazada, el "sig" del último elemento apunta a NULL, luego saber si has llegado al final de la lista es tan sencillo como comprobar el "sig" de cada elemento.

También, mi consejo, es que tiendas a reutilizar código... no tiene sentido que tengas dos funciones para añadir elementos a la lista ( una para añadir al principio y otra para añadir al final ). Tiene más sentido que haya una función única y que sea ésta la que sepa añadir un elemento en cualquier posición... así cuando uses tu librería no tendrás que preocuparte por la posición concreta del elemento a insertar.

También te recomiendo no usar variables globales. Es mejor pasar estas variables como argumentos de las funciones.

Un saludo.





Gracias por la respuesta, muy completa y me funcionò, gracias ademàs por los consejos intentarè aplicarlos lo mas que pueda, tomando que soy algo nuevo en esto de la programaciòn me viene util cada cosa comentada por aqui :)