Problema con inserimento in lista doppiamente concatenata

di il
24 risposte

Problema con inserimento in lista doppiamente concatenata

Salve, volevo implementare la funzione che inserisce gli elementi di un array generati casualmente in lista ordinata. Ho provato a fare la stessa cosa per una lista doppiamente concatenata, quindi ho solo provato a cambiare un pò il codice che già avevo per adattarlo a quello che volevo fare.
Inizialmente funzionava tranquillamente, ma dopo un pò(cioè ogni volta che provavo a far partire il programma ) cominciava ad arrestarsi dicendo che il programma aveva smesso di funzionare, nonostante non avessi modificato più nulla:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct Node  {
	int data;
	struct Node* next;
	struct Node* prev;
}Node;

typedef Node* Nodeptr;
 static Nodeptr head; // puntatore al nodo testa

//Crea nodo e ne restituisce un puntatore
Nodeptr getNewNode(int value) {
	Nodeptr newNode	= (Nodeptr)malloc(sizeof(Node));
	newNode->data = value;
	newNode->prev = NULL;
	newNode->next = NULL;
	return newNode;
}

void insert(int value) {
	Nodeptr temp = head;
	Nodeptr  newNode = getNewNode(value);
	if(head == NULL) {
		head = newNode;
		
		return;
	}
	
	Nodeptr current = head;
	current->prev = NULL;
	Nodeptr previous = NULL;
	
	//trovare la posizione corretta dell'elemento nella lista:
	while( current != NULL && value > current->data){
	previous = current;//assegna il nodo alla lista corrente a quello precedente
	current = current->next;//va avanti al nodo successivo
	
	}
	if(previous == NULL){
		head->prev = newNode;
	    newNode->next = head; 
	    head = newNode;		
	}
	else{//inserisci il nuovo nodo tra previous e current
	     newNode->prev = previous;
	     previous->next = newNode;
		 newNode->next = current;	
         current->prev = newNode;		 
	}
}


void printTesta() {
	Nodeptr  temp = head;
	printf("Dalla testa: ");
	while(temp != NULL) {
		printf("%d--> ",temp->data);
		temp = temp->next;
	}
	puts("NULL\n");
}

void printCoda() {
	Nodeptr temp = head;
	if(temp == NULL) return; 
	
	while(temp->next != NULL) {
		temp = temp->next;
	}
	
	printf("Dalla coda: ");
	while(temp != NULL) {
		printf("%d--> ",temp->data);
		temp = temp->prev;
	}
	printf("NULL\n");
}

Nodeptr arrayToDoubleList(int array[], unsigned int size){
 	if(size == 0) return NULL;
	Nodeptr start = arrayToDoubleList(array, size - 1);
	insert(array[size - 1]);
	return start;
}
int main() {

	srand(time(0));
	unsigned int size;
	
	printf("grandezza aray: ");
	scanf("%u", &size);
	
	int array[size];
	for(size_t i = 0; i < size; i++){
		array[i] = rand() % 300;
		printf("|%d|", array[i]);
	}
	Nodeptr start = arrayToDoubleList(array, size);
	puts("");
	printTesta();
	puts("");
	printCoda();
} 

24 Risposte

  • Re: Problema con inserimento in lista doppiamente concatenata

    Ciao, ma la versione con la lista semplicemente concatenata funziona?
    Il fatto di avere una lista doppiamente concatenata dovrebbe secondo te portare ad un'ottimizzazione dell'inserimento in ordine? Se sì, in che modo la presenza del puntatore prev costituirebbe un vantaggio?

    P.S.
    Non utilizzare variabili globali.
  • Re: Problema con inserimento in lista doppiamente concatenata

    La versione con la lista normale funziona, ma l'esercizio richiede che restituisca una lista doppiamente concatenata, solo per quello.
    Invece per evitare la variabile globale, basterebbe inizializzare il nodo alla testa direttamente nel e passarlo come argomento alla funzione insert giusto?
  • Re: Problema con inserimento in lista doppiamente concatenata

    Questa è quella con link singolo:
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <time.h>
    
    struct listNode{
    	
    	int item;
    	struct listNode * next;
    	
    };	
    typedef struct listNode ListNode;
    typedef ListNode* Nodeptr;
    
    
    
    int isEmpty( Nodeptr s){
    	return s == NULL;
    }
    
    void insert(Nodeptr *s, int value);
    void print(Nodeptr current);
    Nodeptr arrayToDoubleList(int array[], unsigned int size);
    
    
    int main(){
    	srand(time(0));
    	
    	unsigned int size;
    	
    	printf("grandezza aray: ");
    	scanf("%u", &size);
    	
    	int array[size];
    	for(size_t i = 0; i < size; i++){
    		array[i] = rand() % 300;
    		printf("|%d|", array[i]);
    	}
    	Nodeptr start = arrayToDoubleList(array, size);
    	print(start);
        
    	
    }
    
    void insert(Nodeptr *s, int value){
    	Nodeptr new = malloc(sizeof(ListNode));//creazione nodo della lista
    	if(new != NULL){//SE la malloc è andata a buon fine:
    		new->item = value;//inserire il valore nel campo informativo del nodo
    		new->next = NULL;//mettere a NULL il puntatore al prossimo nodo della lista
    	    
    		
    	Nodeptr current = *s;
    	Nodeptr previous = NULL;
    	
    	//trovare la posizione corretta dell'elemento nella lista:
    	while( current != NULL && value > current->item){
    	previous = current;//assegna il nodo alla lista corrente a quello precedente
    	current = current->next;//va avanti al nodo successivo
    	
    	}
    	
    	if(previous == NULL){
    		new->next = *s;
    		*s = new;
    	}
    	else{//inserisci il nuovo nodo tra previous e current
    	    previous->next = new;
    		new->next = current;
    	}
    }
        else{
    	printf("non inserito: memoria non disponibile.\n");
       }
    }
    
    void print(Nodeptr current){
    	if(isEmpty(current)){
    		printf("la lista e' vuota\n\n");
    	}
    	else{
    		puts("\nLa lista e':\n");
    		while(current != NULL){
    			printf("%d-->",current->item);
    			current = current->next;
    		}
    		puts("NULL\n");
    	}
    }
    
    Nodeptr arrayToDoubleList(int array[], unsigned int size){
    	
    		if(size == 0) return NULL;
    	 Nodeptr start = arrayToDoubleList(array, size - 1);
    	 insert(&start, array[size - 1]);
    	 return start;
    }
  • Re: Problema con inserimento in lista doppiamente concatenata

    Ma quest'ultimo codice è tutta farina del tuo sacco? Chiedo perchè noto parecchie differenze rispetto al precedente.
    In ogni caso ecco come diventerebbe il codice che hai appena postato
    - indentandolo meglio;
    - rimuovendo typedef per me fuorvianti;
    - eliminando l'inutile funzione isEmpty() che tu stesso a volte utilizzi e a volte no;
    - rimuovendo l'inutile passaggio per l'array;
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    typedef struct node_
    {
        int item;
        struct node_ *next;
    }   node;
    
    void insert(node **p, int value)
    {
        node* nuovo = (node*)malloc(sizeof(node));
        if(nuovo)
        {
    	nuovo->item = value;
            node* current = *p;
            node* previous = NULL;
    	while(current && value > current->item)
    	{
                previous = current;
                current = current->next;
            }
            if(!previous)
            {
                nuovo->next = *p;
                *p = nuovo;
            }
            else
            {
                nuovo->next = current;
                previous->next = nuovo;
            }
        }
        else
        {
            printf("non inserito: memoria non disponibile.\n");
        }
    }
    
    void print(node *current)
    {
        if(!current)
        {
    	printf("la lista e' vuota\n\n");
        }
        else
        {
    	puts("\nLa lista e':\n");
    	while(current)
    	{
    	    printf("%d-->", current->item);
    	    current = current->next;
    	}
            puts("NULL\n");
        }
    }
    
    int main()
    {
        srand(time(0));
        node *head = NULL;
        unsigned int dim = rand() % 10;
        for(unsigned int i = 0; i < dim; ++i)
        {
    	insert(&head, rand() % 100);
        }
        print(head);
    }
    Ti faccio inoltre notare che la funzione insert() può essere semplificata in
    void insert(node **p, int value)
    {
        node* nuovo = (node*)malloc(sizeof(node));
        if(nuovo)
        {
    	nuovo->item = value;
    	while(*p && value > (*p)->item)
    	{
                p = &(*p)->next;
            }
            nuovo->next = *p;
            *p = nuovo;
        }
        else
        {
            printf("non inserito: memoria non disponibile.\n");
        }
    }
    Detto questo, non vedo come un ulteriore membro prev, in una lista doppiamente concatenata, possa essere d'aiuto in un inserimento in ordine. Quindi l'unica differenza rispetto al caso di lista semplice è che devi aggiornare anche i membri prev dei nodi.
  • Re: Problema con inserimento in lista doppiamente concatenata

    Grazie mille!
    l'esercizio chiedeva l'implementazione della funzione arrayTodoubleList col metodo ricorsivo restituendo una lista doppiamente concatenata, che è effettivamente quello che ho scritto io, il resto invece l'avevo preso da un libro, per questo il codice e' scritto diversamente nel secondo.
    Però effettivamente ho ancora problemi con questi argomenti avendo cominciato da poco con la programmazione in generale.
  • Re: Problema con inserimento in lista doppiamente concatenata

    In tal caso non mi sembra un granché come libro!

    Partiamo dalla seguente funzione valida nel caso di lista semplice:
    void insert(node **p, int value)
    {
        node* nuovo = (node*)malloc(sizeof(node));
        if(nuovo)
        {
    	nuovo->item = value;
    	while(*p && value > (*p)->item)
    	{
                p = &(*p)->next;
            }
            nuovo->next = *p;
            *p = nuovo;
        }
        else
        {
            printf("non inserito: memoria non disponibile.\n");
        }
    }
    Per adattare il codice al caso di lista doppiamente concatenata basta aggiungere due righe di codice.
    Quali? Prova a ragionarci, magari dando un'occhiata al seguente schema



    Le frecce in nero rappresentano i collegamenti iniziali, mentre quelle in rosso sono i collegamenti finali.
  • Re: Problema con inserimento in lista doppiamente concatenata

    Allora dopo che si fa lo scambio, almeno per quello scritto prima, quello che adesso è il next del nuovo nodo inserito è ancora collegato a p tramite prev,quindi una cosa del genere(?): nuovo->prev = nuovo->next->prev (per assegnare p al nuovo->prev praticamente);
    l'altra invece dovrebbe aggiornare proprio nuovo->next->prev in modo che punti al nuovo nodo, che adesso è collegato a p, quindi(?): nuovo->next->prev = *p anche se mi sembra confuso
  • Re: Problema con inserimento in lista doppiamente concatenata

    In linea di massima il ragionamento mi sembra corretto, il problema è che abbiamo ignorato un dettaglio non da poco, ossia che questo approccio funziona soltanto se il nuovo nodo va ad inserirsi tra altri due nodi già esistenti. Infatti se l'elemento viene aggiunto in testa o in coda alla lista, la situazione si complica un po' e soprattutto dipende da come hai impostato la lista doppiamente concatenata. Non so se esiste un'implementazione standard, per questo devo sapere a cosa puntano il membro prev della testa e il membro next della coda. Puntano entrambi a NULL?
  • Re: Problema con inserimento in lista doppiamente concatenata

    Quindi bisognerebbe avere comunque un sempre un riferimento fisso alla testa alla coda, in modo analogo alla variabile globale che avevo usato precedentemente? Comunque quando ho cercato di capire come funzionasse questo tipo di lista, ho visto chi lo faceva usando un'altra struttura che avesse come campo dei link alla testa e alla coda, però questo dovrebbe complicare ancora più la cosa.
  • Re: Problema con inserimento in lista doppiamente concatenata

    Non sono sicuro di aver capito cosa intendi. In ogni caso è così che dovrebbe essere strutturata la lista doppiamente concatenata?

  • Re: Problema con inserimento in lista doppiamente concatenata

    Si intendevo dire che, dato il bisogno di avere comunque i due link alla testa e coda come riferimento fisso, ho visto usare un'altra struttura di supporto per fare ciò, ma che non saprei se c'è comunque un metodo più conveniente per farlo usando sempre la stessa struttura di prima e senza usare variabili globali.
  • Re: Problema con inserimento in lista doppiamente concatenata

    Si potrebbe utilizzare qualcosa del genere:
    typedef struct node_
    {
        int data;
        struct node_ *prev;
        struct node_ *next;
    }   node;
    
    typedef struct list_
    {
        node *head;
        node *tail;
    }   list;
    In tal caso però bisognerebbe ripensare un po' a tutto, in quanto la necessità di aggiornare sia la testa che la coda richiede di passare alla funzione ulteriori argomenti. Il seguente prototipo dovrebbe essere sufficiente:
    void insert(list *l, int value);
    prova a ragionarci e a buttare giù del codice e poi ne discutiamo.
  • Re: Problema con inserimento in lista doppiamente concatenata

    Questa come prima parte della funzione, dovrebbe inizializzare il nodo di testa e quello di coda che dovrebbero coincidere quando la lista è vuota:
    node *nuovo = (node*)malloc(sizeof(node));
     if (nuovo)
       {
        nuovo->data = value;
        nuovo->next = NULL;
        nuovo->prev = NULL;
        l->head = l->tail = nuovo;
        }
        else
        {
           puts("memoria non disponibile.");    
        }
        
  • Re: Problema con inserimento in lista doppiamente concatenata

    Mi sembra un po' poco come punto di partenza per una discussione proficua... prova ad impostare un po' l'intera funzione insert() e poi ne riparliamo.
Devi accedere o registrarti per scrivere nel forum
24 risposte