So there's two ways to reverse a list in place recursively.
First, some setup. Let's make it easy to load linked lists
of strings and print them, so we can make sure this stuff works:
// linked_list.c
#include <stdio.h>
#include <stdlib.h>
// a linked lis of strings
typedef struct S {
struct S * next;
char * val;
} S;
// print out the list
void showS(char * const name, S * head) {
printf("%s: (", name);
while (head){
printf(" ");
printf("%s",head->val);
head = head->next;
printf( "%c", head ? ',' : ' ' );
}
printf(")\n");
}
// convert an array of strings into a linked list of strings
S * mkS(int n, char ** args) {
S * head = NULL;
if (n > 0 && (head = calloc(n, sizeof(S)))){
S * curr = head - 1;
while (n-- > 0) {
curr++;
curr->val = *args++;
curr->next = curr + 1;
}
curr->next = NULL;
}
return head;
}
One way of reversing the list involves passing back the new head of the
list once we find it. We don't need it locally (since we're just moving
the current element to the new end), but we'll need it so that the caller
has a pointer to the head of the list once we're done.
// reverse a list one way
S * revS1( S * const head ){
if (head && head->next) {
S * const new_head = revS1( head->next );
head->next->next = head;
head->next = NULL;
return new_head;
} else {
return head;
}
}
Another way takes a pointer to a pointer. The only difference is that
we don't need to return anything, since we're directly modifying a variable
the caller has. I prefer this calling method since it's much clearer
that we're modifying the list, not returning a copy. It's also harder
for the caller to accidentally loose the pointer to the new head this way.
// reverse a list another way
void revS2( S ** phead ){
S * const head = *phead;
if (head && head->next) {
*phead = head->next;
revS2( phead );
head->next->next = head;
head->next = NULL;
}
}
But what's better than either of these is to reverse the list non-recursively.
Neither of those functions is tail-recursive, so the compiler has
to allocate new stack frames for each element in the list. Try to reverse a long
enough list, and you'll blow your stack. Much better to just reverse the list
using a while loop.
// reverse a list non-recursively
void revS3( S ** phead ){
S * head = *phead;
S * reversed = NULL;
while (head) {
S * curr = head;
head = curr->next;
curr->next = reversed;
reversed = curr;
}
*phead = reversed;
}
Now we can test our results just by building lists out of the command line:
// just use the command line arguments as our list
int main(int argc, char** argv){
S* list1 = mkS(argc - 1, argv + 1);
S* list2 = mkS(argc - 1, argv + 1);
S* list3 = mkS(argc - 1, argv + 1);
showS( "given", list1 );
showS( "revS1", revS1(list1) );
revS2( &list2 );
showS( "revS2", list2 );
revS2( &list3 );
showS( "revS3", list3 );
return 0;
}
So let's compile:
% gcc -Wall linked_list.c -o linked_list
And do some test runs
% ./linked_list
given: ()
revS1: ()
revS2: ()
revS3: ()
% ./linked_list first second third
given: ( first, second, third )
revS1: ( third, second, first )
revS2: ( third, second, first )
revS3: ( third, second, first )
% ./linked_list only
given: ( only )
revS1: ( only )
revS2: ( only )
revS3: ( only )
return ;toreturn NULL;