Reputation: 267
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
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
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
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
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
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
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