Cancellare nodi dispari da una lista

di il
7 risposte

Cancellare nodi dispari da una lista

Ciao a tutti! Sono diverse settimane che provo ad esercitarmi con le liste. Vorrei eliminare dalla lista i nodi contenenti numeri dispari.
Le strutture create sono cosi definite:
typedef struct Nodo{
	int numero;
	struct Nodo *prox;
}Nodo;

typedef Nodo *Lista;
Ho realizzato 2 funzioni. Una che elimina il primo elemento dispari che trova , e un'altra che permette di ripetere questa operazione fin quando si arriva alla fine della lista. Il problema si presenta solo in alcuni casi. Certe volte funziona e certe volte no. Non capisco veramente cosa io abbia sbagliato e credo di star impazzendo. Vi allego il codice con la parte interessata:
int Controllare_Lista_Vuota(Lista l){ //funzione che controllerà se la lista sia vuota. Darà 1 se la lista non è vuota e 0 se la lista è vuota
	int risultato;
	risultato=1;
		if(l==NULL){
			risultato=0;
		}
	return risultato;
}

int Verificare_Divisibile(int val1,int val2){
	int r=val1;
	int divisibile=0;
		while(r>0){
			r=r-val2;
		}
		if(r==0){			
			divisibile=1;
		}
	return divisibile;
}

Lista Cancellare_Singolo_Elemento(Lista l){ //funzione che permette di eliminare il primo elemento dispari che trova
	Nodo *temp;
	Nodo *punt_Corrente;
	Nodo *punt_Precedente;
		if((Verificare_Divisibile(l->numero,2)==0)&&(Controllare_Lista_Vuota(l)==1)){ //se l'elemento è il primo della lista
			temp=l;
			l=l->prox;
			free(temp);
		}
		if((Verificare_Divisibile(l->numero,2)!=0)&&(Controllare_Lista_Vuota(l)==1)){ //se l'elemento non è il primo della lista
			punt_Precedente=NULL;
			punt_Corrente=l;
				while((punt_Corrente!=NULL)&&(Verificare_Divisibile(punt_Corrente->numero,2)!=0)){
					punt_Precedente=punt_Corrente;
					punt_Corrente=punt_Corrente->prox;
				}
			if((punt_Corrente!=NULL)&&(Verificare_Divisibile(punt_Corrente->numero,2)==0)){ 
				temp=punt_Corrente;
				punt_Precedente->prox=punt_Corrente->prox;
				free(temp);
			}
		}
	return l;
}

Lista Cancellare_Dispari(Lista l){
	Nodo *temp;
	temp=l;
		while(temp!=NULL){
			l=Cancellare_Singolo_Elemento(l);
			temp=temp->prox;
		}
	return l;
}

7 Risposte

  • Re: Cancellare nodi dispari da una lista

    Ciao!

    Innanzitutto ti consiglio di lasciar perdere il secondo typedef, visto che tu stesso a volte lo utilizzi e a volte no.

    Per quanto riguarda la funzione Controllare_Lista_Vuota(), premesso che non ha molto senso implementare una funzione per un controllo tanto banale e premesso che stando al nome della funzione i valori di ritorno dovrebbero essere invertiti, ti faccio notare che il tutto può essere semplificato in:
    int Controllare_Lista_Vuota(Lista l)
    {
    	return(l != NULL);
    }
    Per quanto riguarda la funzione Verificare_Divisibile() la domanda sorge spontanea... conosci l'operatore modulo %?

    Per quanto riguarda invece le altre due funzioni ora non ho tempo e quindi neanche ci provo a trovare gli eventuali errori. In ogni caso ti consiglio di separare il concetto di trovare i nodi dispari da quello di cancellare i nodi. In pratica per un codice più chiaro e generico sarebbe meglio implementare una funzione che si occupi soltanto di eliminare un singolo generico nodo.

    Sarei inoltre curioso di vedere come hai implementato l'inserimento in testa e in coda.
  • Re: Cancellare nodi dispari da una lista

    Ciao , grazie per la risposta innanzitutto. Per quanto riguarda la struttura , sfortunatamente mi devo attenuare alle indicazioni del professore e quindi, avendo accordato con i miei colleghi questa struttura precisa ,devo rispettarla.
    Per quanto riguarda la funzione di verificare se il numero è divisibile , so benissimo dell'esistenza della funzione " % " però anche in questo caso, devo attenuarmi alle indicazioni del professore e quindi ne ho dovuta creare una (che fortunatamente funziona).
    Invece per quanto riguarda il realizzare una funzione che cancellasse un nodo ben specifico, ci ho provato e riprovato ma continuava a darmi problemi e non funzionare.
  • Re: Cancellare nodi dispari da una lista

    LollipopBB ha scritto:


    Ciao , grazie per la risposta innanzitutto. Per quanto riguarda la struttura , sfortunatamente mi devo attenuare alle indicazioni del professore e quindi, avendo accordato con i miei colleghi questa struttura precisa ,devo rispettarla.
    Io mi riferivo a
    typedef Nodo *Lista;
    visto che tu stesso a volte utilizzi Lista e a volte Nodo*.

    LollipopBB ha scritto:


    Per quanto riguarda la funzione di verificare se il numero è divisibile , so benissimo dell'esistenza della funzione " % " però anche in questo caso, devo attenuarmi alle indicazioni del professore e quindi ne ho dovuta creare una (che fortunatamente funziona).
    Tipo particolare questo prof!

    LollipopBB ha scritto:


    Invece per quanto riguarda il realizzare una funzione che cancellasse un nodo ben specifico, ci ho provato e riprovato ma continuava a darmi problemi e non funzionare.
    Quando ti ho chiesto di postare la tua implementazione dell'aggiunta in testa e in coda, era proprio per capire il tuo approccio alle liste in modo da aiutarti a realizzare una funzione cancella-nodo. Per esempio relativamente alle liste hai dimestichezza nell'uso di puntatori doppi?

    P.S.
    Si dice "attenersi" non "attenuarsi"!
  • Re: Cancellare nodi dispari da una lista

    La lista la utilizzo nel main quando vado a creare la variabile Lista lista per esempio ecc...
    Per quanto riguarda l'errore grammaticale, so benissimo che si scrive cosi ahah, sinceramente tra lavoro, famiglia ecc non ci ho fatto caso.
    Comunque questa è la funzione di inserimento in lista :
    
    Lista Memorizzare_Numeri_In_Lista(Lista l,int numero){   //funzione che permette di inserire in lista un elemento in testa
    	Nodo *temp;
    	temp=malloc(sizeof(Nodo));
    	temp->numero=numero;
    	temp->prox=l;
    	return temp;
    }
    
    Lista Inserire_Numeri(Lista l){
    	int i;
    	i=0;
    	int n;
    	int valore;
    	Stampare_Messaggio("Quanti numeri vuoi inserire? --> ");
    	n=Acquisire_Numero_Da_Tastiera(n);
    	do{
    		Stampare_Messaggio("\nInserisci il numero da inserire nell'elenco--> ");
    		valore=Acquisire_Numero_Da_Tastiera(valore);
    		l=Memorizzare_Numeri_In_Lista(l,valore);
    		i=i+1;
    	}while(i<n);
    	return l;
    }
  • Re: Cancellare nodi dispari da una lista

    LollipopBB ha scritto:


    La lista la utilizzo nel main quando vado a creare la variabile Lista lista per esempio ecc...
    Certo, utilizzi Lista anche per gli argomenti delle funzioni, mentre in altri casi utilizzi Nodo*, come mai? Forse perchè Lista in quel caso può risultare ambiguo?

    LollipopBB ha scritto:


    Comunque questa è la funzione di inserimento in lista :
    ...
    Innanzitutto le funzioni Acquisire_Numero_Da_Tastiera() e Stampare_Messaggio(), per quanto io non sappia che tipo di controlli tu possa effettuare al loro interno, sono assolutamente inutili e rendono il codice meno chiaro.
    Noto cmq che c'è solo la funzione per l'aggiunta in testa, manca invece quella per l'aggiunta in coda... evidentemente il prof riteneva più importante vietare l'utilizzo di %...
    Ad ogni modo noto che per l'aggiunta in testa tu utilizza una funzione del tipo
    nodo* aggiungi_in_testa(nodo *p, int n);
    e non
    void aggiungi_in_testa(nodo **p, int n);
    Utilizzare come argomento un puntatore doppio, anche se inizialmente potrebbe risultare più ostico, comporta anche notevoli vantaggi.
    Esso infatti, relativamente all'eliminazione di un nodo, potendo contenere sia l'indirizzo della testa della lista sia quello del generico membro prox, non ti costringe a fare distinzioni (come hai fatto nella funzione Cancellare_Singolo_Elemento()) tra il primo elemento della lista e gli altri.

    Prova a ragionarci un po' e fammi sapere!
  • Re: Cancellare nodi dispari da una lista

    Vabbe comunque oltre a queste funzioni (che per il momento funzionano) a me serviva una mano nel capire il problema della cancellazione.
    Grazie comunque per i consigli!
  • Re: Cancellare nodi dispari da una lista

    In realtà ti ho già detto come la penso, ossia che al fine di rendere il codice più chiaro e generico hai bisogno di implementare una funzione cancella_nodo() finalizzata all'eliminazione di un generico nodo. A tal proposito ti ho già dato qualche input, ma evidentemente devo essere più esplicito...
    Ipotizziamo di avere una funzione del genere:
    void cancella_nodo(nodo *curr);
    finalizzata ad eliminare il nodo curr. Cancellare il nodo risulta banale, invece collegare il nodo precedente a curr con quello ad esso successivo risulta impossibile in quanto non abbiamo modo di accedere al membro next del nodo precedente.
    Possiamo quindi ipotizzare di utilizzare ugualmente una funzione del tipo:
    void cancella_nodo(nodo *prec);
    finalizzata però ad eliminare il nodo successivo a prec. In questo caso il collegamento tra il nodo precedente e successivo al nodo da eliminare risulta fattibile, ma sorge un altro problema, ossia non possiamo eliminare il nodo iniziale, in quanto ovviamente non preceduto da un altro nodo.
    Bisogna quindi distinguere l'eliminazione del primo elemento della lista (caso1) dall'eliminazione di uno degli elementi successivi (caso2), e quindi sarà anche necessario escogitare un modo per comunicare alla funzione se ci troviamo nel primo o nel secondo caso. Come fare? Possiamo per esempio ricorrere a qualcosa del genere:
    void cancella_nodo(nodo *testa, nodo *prec);
    dove prec è il nodo precedente al nodo da eliminare e testa rappresenta ovviamente la testa della lista. Il caso1 potrebbe per esempio essere riconosciuto dal fatto che prec sia uguale a NULL.
    Resta però un ultimo problema: come aggiornare la testa della lista nel caso1? Bisogna quindi modificare il prototipo della funzione nel seguente modo:
    nodo* cancella_nodo(nodo *testa, nodo *prec);
    in modo che la funzione ritorni la testa della lista, eventualmente aggiornata se ci troviamo nel caso1.

    L'implementazione della funzione la lascio a te, ma ti mostro come ti semplificherebbe la vita l'utilizzo di un puntatore doppio:
    void cancella_nodo(nodo **curr)
    {
        nodo *temp = *curr;
        *curr = (*curr)->next;
        free(temp);
    }
    Come vedi: semplice, conciso, nessun valore di ritorno e nessuna distinzione tra caso1 e caso2!

    Ovviamente una volta implementata la funzione cancella_nodo(), cancellare i nodi dispari risulterà banale!
Devi accedere o registrarti per scrivere nel forum
7 risposte