NUO
NUO

Reputation: 267

how to use a pointer to pointer to insert in a linked list

Think is a function to insert new element in the order of name. I knew how to do it if I use a if to separate condition of inserting at the start and others. But I was asked to merge the if and while into a single while loop. How could i integrate the insert function into one while loop with pointer to pointer?

person* insert_sorted(person *people, char *name, int age)
{
    person *p=NULL;//,*t=NULL,*q=NULL;
    person *ptr= people;
    person **ptr2ptr=&ptr;

    p=malloc(sizeof(person));

    if ( p == NULL ){
        printf("malloc() failed\n");
        return NULL;
    }
    else {
        p->name = name;
        p->age = age;

        if ( people == NULL ){ // empty list
            people = p;
            people->next =NULL;
        }
        else{
            *ptr2ptr = ptr;
            while( (*ptr2ptr) !=NULL )
            {
                if ( compare_people(p, people)<=0 )  // insert at the start
                    break;
                else if ( (*ptr2ptr)->next == NULL) //insert at the end
                    break;
                else if ( compare_people(*ptr2ptr, p) <=0 && compare_people( p, (*ptr2ptr)->next)<=0 )//insert at the middle
                    break;
                *ptr2ptr = (*ptr2ptr)->next;
            }
            //insert at the end
            p->next =  (*ptr2ptr)->next;
            (*ptr2ptr)->next = p;

        }
    }

Upvotes: 1

Views: 1484

Answers (6)

NUO
NUO

Reputation: 267

here i found the most useful answer to this question:http://www.mvps.org/user32/linkedlist.html

       ptr2ptr = &people;
        while ( *ptr2ptr!=NULL && compare_people(*ptr2ptr,p) ) {
            ptr2ptr = &(*ptr2ptr)->next;
        }
        p->next = *ptr2ptr;
        *ptr2ptr = p;

Upvotes: 0

Frerich Raabe
Frerich Raabe

Reputation: 94329

eInstead of trying to find the person element in the list which has no successor, try to find the first null pointer. Something like this (untested):

void insert_sorted(person **p, char *name, int age)
{
  while (*p) {
    p = &(*p)->next;
  }
  *p = malloc( ... );
  /* ... */
}

This kind of problem is usually best solved with a pen an paper and then drawing a couple of boxes and arrows. The idea is that your 'p' pointer no longer points at a specific person but rather at some pointer which points to a person.

Upvotes: 1

M Oehm
M Oehm

Reputation: 29126

The way you use the pointer to pointer doesn't make use of the indirection. You only write (*ptr2ptr) where you would normally have written ´ptr`.

The idea of using a pointer to a node pointer is that by adding one level of indirection, you are able to access and modify the head pointer from the calling function. If you just pass in a node pointer, all changes to that pointer are local to the insert function and will not update the head pointer of your list in the calling function if necessary.

Your function signature should already pass a pointer to a node pointer:

void insert(person **p, const char *name, int age);

and call it like so:

person *head = NULL;

insert(&head, "Betty", 26);
insert(&head, "Ralph", 23);
insert(&head, "Chuck", 19);
insert(&head, "Alice", 42);
insert(&head, "Simon", 34);

When you enter the fuction, p is the address of head in the calling function. As you iterate through the list with

p = &(*p)->next;

*p hold the address of the next pointer of the previous node. p is a "whence" pointer: It holds the address of the pointer that points to the ode you are processing. That means an empty list isn't a special case any longer.

Your function requires to return the new head pointer. It is easy to forget to assign it and it also adds some redundancy to the call. The pointer-to-pointer approach also fixes this.

Here's how your insertion code could look like with a function that takes a pointer to pointer as argument:

struct person {
    const char *name;
    int age;
    person *next;
};

int compare(const person *p1, const person *p2)
{
    return strcmp(p1->name, p2->name);
}

person *person_new(const char *name, int age)
{
    person *p = malloc(sizeof(*p));

    p->name = name;
    p->age = age;
    p->next = NULL;

    return p;
}

void insert(person **p, const char *name, int age)
{
    person *pnew = person_new(name, age);

    while (*p && compare(*p, pnew) < 0) {
        p = &(*p)->next;
    }

    pnew->next = *p;
    *p = pnew;
}

Upvotes: 0

Jarno Argillander
Jarno Argillander

Reputation: 5945

Without testing:

person* insert_sorted(person** people, char *name, int age) {
    person* added = malloc(sizeof(person));
    added->name = name;
    added->age = age;
    added->next = NULL;

    person* previous = NULL;
    person* current = *people;
    while (current && compare_people(current, added) <= 0) {
        previous = current;
        current = current->next;
    }

    if (!people) {
        *people = added;
    } else {
        previous->next = added;
        added->next = current;
    }

    return added;   
}

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

The function can be written the following way (without testing because I do not know some definitions of the list)

person * insert_sorted( person **people, char *name, int age )
{
    person *p = malloc( sizeof( person ) );

    if ( p == NULL )
    {
        printf( "malloc() failed\n" );
    }
    else 
    {
        p->name = name;
        p->age = age;

        person *prev = NULL;
        person *current = *people;

        while ( current && !( compare_people( p, current ) < 0 ) )
        {
            prev = current;
            current = current->next;
        }

        p->next = current;
        if ( prev == NULL ) *people = p;
        else prev->next = p;
    }

    return p;
}    

And the function should be called like

insert_sorted( &people, name, age );
               ^^^^^^^

Upvotes: 0

EnzoR
EnzoR

Reputation: 3346

There can be a few options.

I would move the if inside the compare_people function provided that you can change it. After all, adding the very first element in a list is like adding a new "top of the list" element (of least of the list). I know this can be seen as "cheating". And it is, indeed!

You can create a "fake" list element which will always be tested to be the first (or the last) of the sorted list (like with an empty name). So the list won't ever be empty and there won't ever be a "check for an empty list" test. Of course the content of that fake item needs to comply with the semantics of the compare_people function.

At a cost that's slightly higher than the current O(n), O(n*log(n)) actually, you could use a temporary support structure (like an array of pointers) and qsort() from stdlib.h in order to keep the list sorted.

Finally, implement insertion sort which would exploit the fact that the original set is already sorted before inserting the new element.

Upvotes: 0

Related Questions