简体   繁体   English

使用C中的递归技术来反向链接列表

[英]Reverse a linked list using recursion technique in C

I have the following C code which returns the reverse of a linked list. 我有以下C代码,它返回链表的反向链接。

Though it reverses the linked list, I never get the head of the reversed linked list because the restofElements node is getting overwritten. 尽管它反转了链表,但我从来没有得到反向链表的头,因为restofElements节点将被覆盖。

S *reverseRecursive(S *headref) {
    S *firstElement   = NULL;
    S *restOfElements = NULL;

    if (headref==NULL) {
        return ;
    }

    firstElement = headref;
    restOfElements = headref->next;

    if (restOfElements == NULL)
        return headref;   

    reverseRecursive(restOfElements);    
    firstElement->next->next  = firstElement;
    firstElement->next  = NULL;          
    headref = restOfElements; 

    return headref; 
} 

How can I get the head of the reversed linked list node returned to the calling program? 如何获得反向链接列表节点的头并返回到调用程序?

If you want to change the head pointer, you must pass it by reference (as a pointer). 如果要更改头指针,则必须按引用传递它(作为指针)。 The prototype should be modified to receive the head as S **. 应该修改原型以将头部接收为S **。

S *reverseRecursive(S **headref);

The head of the reversed list is equal to the head of the reversed List starting with restOfElements (because the original headref has to become tha last element of the reversed list). 反向列表的头部等于以restOfElements开头的反向列表的头部(因为原始headref必须成为反向列表的最后一个元素)。 So storing the result of the recussive call should do (as Jim has already suggested in his comment): 因此应该存储回拨电话的结果(就像吉姆已经在他的评论中建议的那样):

...
headref = reverseRecursive(restOfElements);  
firstElement->next->next  = firstElement;
firstElement->next  = NULL;          
/* headref = restOfElements; that's wrong */    
return headref; 

Probably closer. 可能更近了。

S *reverseRecursive(S *headref)
 {
  S *firstElement   = NULL;
  S *restOfElements = NULL;
  S *new_head = NULL;
  if (headref==NULL)
    {
    return ;
    }
  firstElement = headref;
  restOfElements = headref->next;
  if (restOfElements == NULL)
       return headref;   
  new_head = reverseRecursive(restOfElements); 
  restOfElements->next = new_head;           
  return restOfElements;
} 

Thanks everyone. 感谢大家。 I have modified it little bit and it works now. 我已经对其进行了一点修改,现在可以使用了。 Let me know your comments. 让我知道你的评论。 new_head is a global variable. new_head是全局变量。

S *reverseRecursive(S *headref)
 {
  S *firstElement   = NULL;
  S *restOfElements = NULL;

  if (headref==NULL)
    {
    return ;
    }
    firstElement = headref;


   if (headref->next == NULL)
      return headref;   
   else
    restOfElements = headref->next;


   reverseRecursive(restOfElements);

   firstElement->next->next  = firstElement;

   firstElement->next  = NULL;          

   if(new_head == NULL ) //just dont take it ervery time
      new_head = restOfElements;

   return new_head; 

  }

$ gcc -std=c99 -Wall -Wextra reverse.c $ gcc -std = c99 -Wall -Wextra reverse.c

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

typedef struct list {
  struct list* next;
  int data;
} S;

void print_list(S* list) {
  if (list == NULL) { printf("NULL\n"); return; }
  printf("%d ", list->data);
  print_list(list->next);
}

S* reverse_aux(S* list, S* tail) {
  // invalid arg
  if (list == NULL) { return NULL; }

  // base case
  if (list->next == NULL) {
    list->next = tail;
    return list;
  }

  // general case
  S* tmp = list->next;
  list->next = tail;

  return reverse_aux(tmp, list);
}

S* reverse(S* list) { return reverse_aux(list, NULL); }

int main(int argc, char* argv[]) {
  // build a list with which to test
  S a[10];
  for (unsigned i = 0; i < sizeof(a)/sizeof(S); ++i) {
    a[i].data = i;
    a[i].next = &a[i+1];
  }
  a[sizeof(a)/sizeof(S) - 1].next = NULL;
  S* list = &a[0];

  print_list(list);
  list = reverse(list);
  print_list(list);

  return 0;
}

Actually, since reverse is destructive (it mutates its argument), a better interface design would probably be 实际上,由于反向是破坏性的(改变其参数),因此更好的接口设计可能是

void reverse(S** plist);
reverse(&list);

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. 使用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;
}

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 )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM