【问题标题】:Reversing a Linked List using recursion in C在 C 中使用递归反转链表
【发布时间】:2017-08-16 13:21:02
【问题描述】:

我是 C 的新手,我正在尝试创建一个函数来反转链表,仅将 List 本身作为参数传递。如果不将节点作为参数传递,这可能吗?

到目前为止,这是我的代码,我知道它不能正常工作,因为我不知道如何对列表的其余部分进行递归调用。

void reverse(LL_t *L) {
    if (L->head->next == NULL) {
        return;
    }

    node_t *rest = L->head->next;
    reverse(rest);
    node_t *q = rest->next;
    q->next = rest;
    rest->next = NULL;
}

这也是我的类型定义。

typedef struct {
    node_t *head;
    node_t *tail;
} LL_t;

typedef struct _node {
    int data;
    struct _node *next;
} node_t;

【问题讨论】:

  • 编写另一个从节点进行反转的函数(未在 API 中公开)。
  • reverse(rest); 类型不匹配。 reverse 需要 LL_t*rest 的类型是 node_t*
  • @BLUEPIXY 我不确定如何将“休息”变成 LL_t*,以便它包含除当前头部之外的所有元素
  • 不需要将其设为递归调用,但对于递归调用,LL_t 只是一个持有者,因此使用node_t 作为参数是有意义的。
  • 与其将reverse本身作为递归函数,不如将补充函数(例如void reverse_aux(LL_t*, node_t*)等)作为递归函数呢?

标签: c recursion linked-list


【解决方案1】:

您可以通过简单的循环来反转列表,不需要递归并且给定您的 API,不合适。

这是你的函数的修改版本:

void reverse(LL_t *L) {
    node_t *prev = NULL;
    node_t *curr = L->head;
    L->tail = curr;
    while (curr != NULL) {
        node_t *next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }
    L->head = prev;
}

如果你需要使用递归,你可以测试列表是否为空或限制为单例而不做任何事情,否则移除头部元素,反转结果列表并将元素追加到末尾:

void reverse(LL_t *L) {
    if (L->head != L->tail) {
        /* at least 2 elements */
        node_t *node = L->head;
        L->head = node->next;
        node->next = NULL;
        reverse(L);
        L->tail = L->tail->next = node;
    }
}

请注意,如果列表太长,这种递归方法可能会出现未定义的行为,因为reverse 将递归太多次并导致堆栈溢出

【讨论】:

    【解决方案2】:
    struct node {
       int          value;
       struct node* next;
    };
    

    在常量堆栈空间中操作的非递归定义:

    void reverse(struct node** ptr) {
       struct node* prev_ptr;
       struct node* node_ptr;
    
       if (prev_ptr = * ptr) {
          node_ptr = prev_ptr -> next;
    
          prev_ptr -> next = NULL;
    
          while (node_ptr) {
             struct node* temp = node_ptr -> next;
    
             node_ptr -> next = prev_ptr;
    
             prev_ptr = node_ptr;
             node_ptr = temp;
          }
    
          * ptr = prev_ptr;
       }
    }
    

    扩展等价的递归定义:

    void reverse(struct node** ptr) {
       struct node* node_ptr;
    
       if (node_ptr = * ptr) {
          node_ptr -> next = NULL;
    
          * ptr = reverse_rec(node_ptr, node_ptr -> next);
       }
    }
    
    struct node* reverse_rec(struct node* prev_ptr, struct node* node_ptr) {
       if (! node_ptr) { return prev_ptr; }
    
       struct node* temp = reverse_rec(node_ptr, node_ptr -> next);
       node_ptr -> next = prev_ptr;
       return temp;
    }
    

    【讨论】:

    • reverse() 在哪里更新 list.tail?递归在哪里(问题陈述需要递归)?
    • @rcgldr 在它的身体里?正如答案:递归在这里没有意义。
    • @rcgldr 现在看看:太可怕了。
    【解决方案3】:

    这可行,但使用递归来反转列表需要 O(n) 堆栈空间开销。这里的概念是推进 L->head 的静态实例,同时为每一级递归保留一个 head 的本地副本。递归一直持续到到达列表的末尾,然后使用 head 的本地实例反转列表,因为 reverse() 返回备份调用链。

    void reverse(LL_t *L)
    {
    node_t *head;
        if(L->head == NULL || L->head->next == NULL)
            return;
        head = L->head;
        L->head = head->next;
        reverse(L);
        head->next->next = head;   // reverse the nodes
        head->next = NULL;
        L->tail = head;            // ends up setting tail to what was 1st node
    }
    

    【讨论】:

      【解决方案4】:

      //反转链表的简单程序

      void 反向(结构节点* head_ref) { 首先是结构节点*;

      struct node* rest;
      if (head_ref == NULL)
         return; 
      
      first = head_ref;  
      rest  = first->next; 
      if (rest == NULL)
         return;   
      
      reverse(rest); 
      first->next->next  = first;      
      first->next  = NULL;          
      head_ref = rest;              
      

      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-14
        • 1970-01-01
        • 2020-07-22
        • 2013-07-27
        • 2018-11-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多