0

I'm working on a function that find a sublist of consecutive value (es 1, 2 3) and sum these values. With an example it will be clearer.

Example: Original List: 10 1 2 3

After function: 10 6

So this is my code (it doesn't work now):

struct nodo * sequenza(struct nodo * testa) //testa = top of list, i'm italian
{   

struct nodo * current, *app, *tmp;

if(testa == NULL || testa->next == NULL) {
    return testa;
}

else
{
    current = testa;
    
    while(current && current->next) {
        app = current;
        while((app && app->next) && (app->valore +1 == app->next->valore)) {
            current->valore += (app->next->valore);
            
            tmp = app->next;
            free(app);
            app = tmp;
            
            //if i just write app = app->next, it works, but i have a memory leak
        }
        
        if(current != app) current->next = app->next;
        current = current->next;
    }
}

return testa;       
}

If I try with "10 1 2 3", i have "10 3 3" in output. If I try with "1 2 3 4 5", i have "12 5" in output. So the problem is how to use free function.

Luigi V.
  • 79
  • 7
  • 3
    Welcome to Stack Overflow. Please read [the help pages](http://stackoverflow.com/help), take the SO [tour], read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). Lastly please [edit] your question to tell us *what* is wrong with your code. *How* doesn't it work now? For some specific input, what is the expected and actual output? Have you tried to *debug* your code? And please try to create a [mcve] to show us. – Some programmer dude Jul 02 '20 at 07:30
  • 2
    You have made the problem more difficult by only passing a pointer to `testa`, instead of the address of `testa` as a parameter. If you have a list `1, 2, 3, 10`, it makes it impossible to replace the first node in the list. What you would like to do is use the address of the pointer as well as the pointer to check whether the value of the next node is 1-more than the current, and if so, add the value of the current `valore` to a temporary `sum` and replace the node at the current address with the next node and delete the current. This lets you iterate and sum while removing nodes - simply. – David C. Rankin Jul 02 '20 at 07:37
  • To add to @David's remark, another approach would be to have a separate struct (`struct list`) which would contain the pointer to the top node. Then you can pass the pointer to this parent struct around and change the head node, without having to pass a pointer to a pointer. Semantically, a list might be more than just "a pointer to the first node" -- e.g this allows you to store the current items count and any additional metadata (callbacks, destructors, whatever). – vgru Jul 02 '20 at 07:47
  • The line `if(current != app) current->next = app->next;` doesn't make sense. If you do `free(app);` and `current` is equal to `app`, you are basically freeing `current`. You should simply write `current = app;` at the end and then `if (current != NULL) current = current->next;`, or, even better, just get rid of the `app` variable and rewrite the loop carefully. – vgru Jul 02 '20 at 07:57
  • Ok let me try and and I'll let you know – Luigi V. Jul 02 '20 at 14:55
  • I deleted app variable and this is the code `current->valore += (current->next->valore);` then `tmp = current->next->next;` then `free(current->next);` and `current->next = tmp;` this is the code in the while. At the end i have your if...But now if i insert (1 2 3 4 5) i have (3 7 5) – Luigi V. Jul 02 '20 at 16:03

1 Answers1

0

The biggest issue with your current approach surrounds what happens if the first node in the list is part of the sequence and must be removed? When you pass a pointer to sequenza() by-value, there is no way to change the address of the first node. Instead, simply pass the address of the pointer to sequenza() which makes compressing sequences in your list trivial. Why approach it this way? See Linus on Understanding Pointers

When you use the address of node to iterate over the list, there is no need to keep track of prev, current, or next values -- there are no special cases. You just check if (node->valero + 1 == node->next->valero) and if so, add the current value to a temporary sum and replace the node at the current address with the next node in the list and free the struct that you replaced. If the next node is not in sequence, just add the sum to the current value and continue iterating over your list. One note: before you check the value of node->next->valero you need to ensure node->next != NULL.

Putting it altogether, you could do:

/** compress sequences of nodes into a single node */
void sequenza (struct nodo **list)
{
    nodo **ppn = list,              /* pointer to pointer to head */
          *pn = *list;              /* pointer to head */
    long sum = 0;                   /* var to hold sum */
    
    while (pn) {    /* loop over each node */
        /* if next node not null and next value in sequnce */
        if (pn->next && pn->valore + 1 == pn->next->valore) {
            sum += pn->valore;      /* add current to sum */
            *ppn = pn->next;        /* replace node at current address with next */
            free (pn);              /* free current node */
            pn = *ppn;              /* initialize current node pointer */
        }
        else {  /* not in sequnce */
            if (sum) {              /* if sum not zero */
                pn->valore += sum;  /* add sum to current value */
                sum = 0;            /* reset sum zero */
            }
            ppn = &pn->next;        /* get address of next node */
            pn = pn->next;          /* get pointer to next node */
        }
    }
}

A full example reading list values from stdin and calling sequenza() on the list could be:

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

typedef struct nodo {     /* list node */
    int valore;
    struct nodo *next;
} nodo;

/** add node at end of list */
nodo *add (nodo **head, int v)
{
    nodo **ppn = head,                      /* pointer to pointer to head */
          *pn = *head,                      /* pointer to head */
          *node = malloc (sizeof *node);    /* allocate new node */

    if (!node) {                            /* validate allocation */
        perror ("malloc-node");
        return NULL;
    }
    node->valore = v;                       /* initialize members values */
    node->next = NULL;

    while (pn) {
        ppn = &pn->next;
        pn = pn->next;
    }

    return *ppn = node;     /* add & return new node */
}

/** compress sequences of nodes into a single node */
void sequenza (struct nodo **list)
{
    nodo **ppn = list,              /* pointer to pointer to head */
          *pn = *list;              /* pointer to head */
    long sum = 0;                   /* var to hold sum */
    
    while (pn) {    /* loop over each node */
        /* if next node not null and next value in sequnce */
        if (pn->next && pn->valore + 1 == pn->next->valore) {
            sum += pn->valore;      /* add current to sum */
            *ppn = pn->next;        /* replace node at current address with next */
            free (pn);              /* free current node */
            pn = *ppn;              /* initialize current node pointer */
        }
        else {  /* not in sequnce */
            if (sum) {              /* if sum not zero */
                pn->valore += sum;  /* add sum to current value */
                sum = 0;            /* reset sum zero */
            }
            ppn = &pn->next;        /* get address of next node */
            pn = pn->next;          /* get pointer to next node */
        }
    }
}

/** print all nodes in list */
void prn (nodo *l)
{
    if (!l) {
        puts ("list-empty");
        return;
    }
    for (nodo *n = l; n; n = n->next)
        printf (" %d", n->valore);
    putchar ('\n');
}

/** delete all nodes in list */
void del_list (nodo *l)
{
    nodo *n = l;
    while (n) {
        nodo *victim = n;
        n = n->next;
        free (victim);
    }
}

int main (void) {

    int v;
    nodo *list = NULL;

    while (scanf ("%d", &v) == 1)
        add (&list, v);
            
    puts ("\noriginal list:");
    prn (list);
    
    sequenza (&list);
    
    puts ("\nsequenza sum:");
    prn (list);
    
    del_list(list);
}

Example Use/Output

$ echo "10 1 2 3" | ./bin/llsequenza

original list:
 10 1 2 3

sequenza sum:
 10 6

or

$ echo "10 1 2 3 5 7 8 9 11" | ./bin/llsequenza

original list:
 10 1 2 3 5 7 8 9 11

sequenza sum:
 10 6 5 24 11

or

$ echo "1 2 3 4 5 6 7 8 9 10" | ./bin/llsequenza

original list:
 1 2 3 4 5 6 7 8 9 10

sequenza sum:
 55

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thank you very much, this program is just perfect!..so I have to study double pointers, because I haven't experience with them.. – Luigi V. Jul 03 '20 at 07:07
  • Think of it this way. In the answer, you are passing the address-of-a-pointer as the parameter. So you add one additional level of pointer-indirection (one more `'*'`). What you have is the address where the current pointer is stored. That allows you to grab the next pointer and stick it in that address. That just changes which node is stored there. Once you move the next pointer in the list back to the address where the current pointer is stored -- you replace the current node at that address with the next one (leaving you free to delete the stuct that was replaced) Makes lists easier. – David C. Rankin Jul 03 '20 at 07:27
  • A few links that provide basic discussions of pointers may help. [Difference between char *pp and (char*) p?](https://stackoverflow.com/a/60519053/3422102) and [Pointer to pointer of structs indexing out of bounds(?)...](https://stackoverflow.com/a/60639540/3422102) – David C. Rankin Jul 03 '20 at 07:29