Concordo con Antonio (apatriarca). Un nodo non è una lista, e risulta importante fare la distinzione. Un'altro modo per separare le due cose è definire la lista come una struct che contiene il puntatore al primo nodo. Insomma così:
- Codice:
struct nodo
{
struct nodo * successivo;
int valore;
};
struct lista
{
struct nodo * testa;
};
eventualmente puoi mettere i typedef se vuoi evitare di dover sempre scrivere
struct
prima del nome.
Suppongo che
suf_insert
aggiunga l'elemento alla fine della lista. Ho già espresso il mio dissenso verso questa funzione nella discussione precedente. Ogni volta che chiami
suf_insert
su una lista, la scorri tutta dalla testa alla coda e infine aggiungi un elemento. Una lista non sa quanto è lunga e conosce la posizione solo del suo elemento iniziale, ma questo non vuol dire che il tuo programma non possa memorizzarti il valore della coda e riutilizzarlo.
Il mio suggerimento consiste nell'avere le seguenti 3 funzioni:
- Codice:
/*!
* @brief Crea una nuova lista da un'altra lista aggiungendo un elemento in
* testa
* @param[in] Valore il valore che verrà salvato nel nuovo primo elemento
* @param[in] Lista la lista che verra' accodata al nuovo elemento creato
* @return la nuova lista con il nuovo valore in testa e la vecchia lista in
* coda
*/
struct lista aggiungi_in_testa(int Valore, struct lista Lista);
/*!
* @brief Aggiunge un nuovo elemento tra un nodo e il suo successivo
* @param[in] Nodo il nodo a cui vuoi collegare il nuovo elemento creato
* @param[in] Valore il valore del nuovo elemento creato
* @return un puntatore al nuovo elemento creato
*
* @note il nodo a cui punta la variabile Nodo sarà modificato per contenere in
* successivo l'indirizzo al nuovo elemento creato. Il nuovo elemento
* creato avra' come successivo l'attuale successivo al nodo puntato da
* Nodo
*/
struct nodo * aggiungi_dopo(struct nodo * Nodo, int Valore);
/*!
* @brief trova l'ultimo elemento di una lista
* @param[in] Lista la lista in cui si cerca l'ultimo elemento
* @return il nodo in coda alla lista
*/
struct nodo * trova_ultimo_elemento(struct lista Lista);
Nota che ho deciso di non modificare l'oggetto lista all'interno della funzione ma di ritornare il nuovo oggetto lista. Pertanto queste funzioni non hanno alcun puntatore a
struct lista
. In altre parole, quando aggiungi un elemento all'inizio della lista, dovrai fare:
- Codice:
Lista = aggiungi_in_testa(1, Lista);
invece di
- Codice:
aggiungi_in_testa_v2(Lista, 1);
dove
aggiungi_in_testa_v2
sarà definita come
- Codice:
void aggiungi_in_testa_v2(struct lista * Lista, int Valore);
e fa la stessa cosa dell'altra funzione.
Supponi quindi di voler aggiungere
3
e
4
alla fine della lista, allora con la mia interfaccia scriverai
- Codice:
struct nodo last = trova_ultimo_elemento( Lista );
last = aggiungi_dopo( last, 3 );
last = aggiungi_dopo( last, 4 );
invece di
- Codice:
suf_insert(Lista->testa, 3);
suf_insert(Lista->testa, 4);
che però è equivalente a fare
- Codice:
struct nodo last = trova_ultimo_elemento(Lista);
aggiungi_dopo(last, 3);
last = trova_ultimo_elemento(Lista);
aggiungi_dopo(last, 4);
Insomma stai facendo una operazione \(\displaystyle O(n) \) in più che però non hai bisogno di fare. Supponi per esempio di dover aggiungere \(\displaystyle 1000 \) elemento alla fine di una lista che ne contiene \(\displaystyle 2000 \). Ignorando la creazione degli elementi (che è piuttosto lenta e va fatta da entrambe le versioni), l'ordine delle operazioni della mia versione è circa \(\displaystyle 3000 \) (\(\displaystyle 2000 \) per
trova_ultimo_elemento
e \(\displaystyle 1000 \) per gli inserimenti), mentre se usi
suf_insert
fai \(\displaystyle 2000+2001+\dotsb+2999+1000 = 1000 + 1500\cdot 2999 - 1000 \cdot 1999 = 2500500 \) operazioni per fare
esattamente la stessa cosa.
Detto questo sarebbe utile sapere qual'è il testo del tuo problema.