繁体   English   中英

使用递归从尾部开始反转链表

[英]Reversing a linked list starting from the tail using recursion

我仍然难以解决递归技术。 我知道下面有一个更好的方法来解决我的问题,即反向链接列表。 我所见的大多数方法都是通过使用迭代或递归从头到尾开始反转指针。

我想通过首先递归地找到列表中的最后一个节点,然后每次函数返回时更改指针,来反转列表。

我到底在做什么错呢? 还是这种方法甚至可以在无需将更多参数传递给递归函数的情况下工作? 在此先感谢您的帮助。

struct Node
{
    int data;
    struct Node *next;
};

Node* Reverse(Node *head)
{
    static Node* firstNode = head;
    // if no list return head
    if (head == NULL)
    {
        return head;
    }

    Node* prev = NULL;
    Node* cur = head;

    // reached last node in the list, return head
    if (cur->next == NULL)
    {
        head = cur;
        return head;
    }

    prev = cur;
    cur = cur->next;
    Reverse(cur)->next = prev;

    if (cur == firstNode)
    {
        cur->next = NULL;
        return head;
    }
    return cur;
}

编辑:另一种尝试

Node* ReverseFromTail(Node* prev, Node* cur, Node** head);

Node* ReverseInit(Node** head)
{
    Node* newHead = ReverseFromTail(*head, *head, head);
    return newHead;
}



Node* ReverseFromTail(Node* prev, Node* cur, Node** head)
{
    static int counter = 0;
    counter++;

    // If not a valid list, return head
    if (head == NULL)
    {
        return *head;
    }

    // Reached end of list, start reversing pointers
    if (cur->next == NULL)
    {
        *head = cur;
        return cur;
    }


    Node* retNode = ReverseFromTail(cur, cur->next, head);
    retNode->next = cur;


    // Just to force termination of recursion when it should. Not a permanent solution 
    if (counter == 3)
    {
        cur->next = NULL;
        return *head;
    }

    return retNode;
}

终于解决了:

Node* NewestReverseInit(Node* head)
{
    // Invalid List, return
    if (!head)
    {
        return head;
    }

    Node* headNode = NewestReverse(head, head, &head);

    return headNode;
}

Node* NewestReverse(Node *cur, Node* prev, Node** head)
{
    // reached last node in the list, set new head and return
    if (cur->next == NULL)
    {
        *head = cur;
        return cur;
    }

    NewestReverse(cur->next, cur, head)->next = cur;

    // Returned to the first node where cur = prev from initial call
    if (cur == prev)
    {
        cur->next = NULL;
        return *head;
    }

    return cur;
}

我不会给你代码,我会给你想法。 您可以在代码中实现该想法。

所有递归问题的关键是弄清两种情况:重复步骤和结束情况。 完成此操作后,它几乎可以像魔术一样工作。

将此原理应用于反转链表:

  • 结束案例:一个元素的列表已经被反转(这很简单)并返回元素本身
  • 重复情况:给定列表L,反转最少意味着反转L',其中L'是L'是不包含第一个元素(通常称为head )的列表,然后将head添加为列表的最后一个元素。 返回值将与您刚刚进行的递归调用的返回值相同。

可以办到。 理解递归的关键是起点?

通常,我会创建一个“开始”函数来准备第一个调用。 有时,它是一个单独的功能(如底部的非OO实现)。 有时,这只是一个特殊的首次调用(如下面的示例所示)。

关键还在于在变量发生变化之前记住变量以及什么是new head

head是列表的最后一个元素。 因此,您必须从列表底部开始。

next元素始终是您的父母。

然后,诀窍是按照正确的顺序进行所有操作。

    Node* Reverse( Node* parent) // Member function of Node.
    {
        Node* new_head = next ? next->Reverse( this ) 
                              : this;

        next = parent;

        return new_head;
    }

您可以使用以下函数调用该函数: var.Reverse( nullptr);

例:

int main()
{
    Node d{ 4, nullptr };
    Node c{ 3, &d };
    Node b{ 2, &c };
    Node a{ 1, &b };

    Node* reversed = a.Reverse( nullptr );
}

那么这里发生了什么?

首先我们创建一个链表:

 a->b->c->d->nullptr

然后函数调用:

  1. a.Reverse(nullptr)被调用。
  2. 这要求在Reverse上的下一个节点b.Reverse与父母a
  3. 这将在下一个节点c.Reverse与父级b调用Reverse
  4. 这将在下一个节点d.Reverse与父级c调用Reverse
  5. d没有next节点,因此它说新的头部本身。
  6. dnext是父c
  7. d将自己返回为new_head
  8. 返回c :从d返回的new_headd
  9. cnext是父b
  10. c返回从d接收到的new_head
  11. 返回b :从c返回的new_headd
  12. bnext现在是父级a
  13. b返回从c接收到的new_head
  14. 返回到a :从b返回a new_headd
  15. anext ,现在它的父nullptr
  16. a返回从b收到的new_head
  17. d返回

非面向对象的实现;

Node* reverse_impl(Node* parent)
{
    Node* curr = parent->next;
    Node* next = curr->next;

    Node* new_head = next ? reverse_impl( curr ) 
                          : curr;

    curr->next = parent;

    return new_head;
}

Node* reverse(Node* start)
{
    if ( !start )
        return nullptr;

    Node* new_head = reverse_impl( start );
    start->next    = nullptr;

    return new_head;
}

这是我在5分钟内编写的完整实现:

#include <stdio.h>

struct Node
{
     int data;
     struct Node *next;
};

struct Node* Reverse(struct Node *n)
{
    static struct Node *first = NULL;

    if(first == NULL)
      first = n;

    // reached last node in the list
    if (n->next == NULL)
      return n;

    Reverse(n->next)->next = n;

    if(n == first)
    {
      n->next = NULL;
      first = NULL;     
    }

    return n;
}

void linked_list_walk(struct Node* n)
{
  printf("%d", n->data);
  if(n->next)
    linked_list_walk(n->next);
  else
    printf("\n");
}

int main()
{
  struct Node n[10];
  int i;

  for(i=0;i<10;i++)
  {
    n[i].data = i;
    n[i].next = n + i + 1;
  }
  n[9].next = NULL;

  linked_list_walk(n);
  Reverse(n);
  linked_list_walk(n+9);
}

输出:

0123456789                                                                                                                                                                          
9876543210 

暂无
暂无

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

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