所以有两种方法可以递归地反转列表。
首先,一些设置。让我们可以轻松地加载和打印字符串的链接列表,这样我们就可以确保这些东西有效:
// 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;
}
反转列表的一种方法是在我们找到列表的新头部后将其传回。我们在本地不需要它(因为我们只是将当前元素移动到新的一端),但是我们需要它以便调用者在我们完成后有一个指向列表头部的指针。
// 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;
}
}
另一种方式采用指向指针的指针。唯一的区别是我们不需要返回任何东西,因为我们直接修改了调用者拥有的变量。我更喜欢这种调用方法,因为更清楚的是我们正在修改列表,而不是返回副本。调用者也更难以这种方式意外丢失指向新头的指针。
// 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;
}
}
但比这两种方法更好的是非递归地反转列表。这些函数都不是尾递归的,因此编译器必须为列表中的每个元素分配新的堆栈帧。尝试反转一个足够长的列表,你会炸掉你的筹码。使用 while 循环来反转列表要好得多。
// 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;
}
现在我们可以通过在命令行中构建列表来测试我们的结果:
// 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;
}
所以让我们编译:
% gcc -Wall linked_list.c -o linked_list
并进行一些试运行
% ./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 )