使用递归从尾部开始反转链表
Reversing a linked list starting from the tail using recursion
我仍然在使用递归技术来解决问题。我知道有更好的方法可以解决下面反转链表的问题。我见过的大多数方法都是通过使用迭代或递归从头到尾来反转指针。
我试图通过首先递归地找到列表中的最后一个节点然后每次函数 returns.
更改指针来反转列表
下面我到底做错了什么?或者这种方法甚至可以工作,而不需要将更多参数传递给递归函数?预先感谢您的帮助。
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;
}
这是我在 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
我不给你代码,我给你思路。你可以在代码中实现这个想法。
所有递归问题的关键是弄清楚两种情况:重复步骤和结束情况。一旦你这样做了,它就像变魔术一样有效。
将此原理应用于反转链表:
- 最终情况:一个元素的列表已经反转(这很简单)并且return元素本身
- 重复情况:给定列表 L,反转这个最少意味着反转 L',其中 L' 是 L' 是没有第一个元素的列表(通常称为
head
),而不是添加head 作为列表的最后一个元素。 Return 值将与您刚刚进行的递归调用的 return 值相同。
可以做到。理解递归的关键是起点是什么?
通常我会创建一个 "starting" 函数来准备第一次调用。有时它是一个单独的函数(比如在底部的非 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
然后函数调用:
a.Reverse(nullptr)
被调用。
- 这会在父节点
a
的下一个节点 b.Reverse
上调用 Reverse
。
- 这会在父节点
b
的下一个节点 c.Reverse
上调用 Reverse
。
- 这会在父节点
c
的下一个节点 d.Reverse
上调用 Reverse
。
d
没有 next
节点所以它说新头就是它自己。
d
的 next
现在是父级 c
d
returns 本身就是 new_head
.
- 返回
c
:从 d
返回的 new_head
是 d
c
的 next
现在是父级 b
c
returns 从 d
收到的 new_head
- 返回
b
:从 c
返回的 new_head
是 d
b
的 next
现在是父级 a
b
returns 从 c
收到的 new_head
- 返回
a
:从 b
返回的 new_head
是 d
a
的 next
现在是父级 nullptr
a
returns 从 b
收到的 new_head
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;
}
我仍然在使用递归技术来解决问题。我知道有更好的方法可以解决下面反转链表的问题。我见过的大多数方法都是通过使用迭代或递归从头到尾来反转指针。
我试图通过首先递归地找到列表中的最后一个节点然后每次函数 returns.
更改指针来反转列表下面我到底做错了什么?或者这种方法甚至可以工作,而不需要将更多参数传递给递归函数?预先感谢您的帮助。
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;
}
这是我在 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
我不给你代码,我给你思路。你可以在代码中实现这个想法。
所有递归问题的关键是弄清楚两种情况:重复步骤和结束情况。一旦你这样做了,它就像变魔术一样有效。
将此原理应用于反转链表:
- 最终情况:一个元素的列表已经反转(这很简单)并且return元素本身
- 重复情况:给定列表 L,反转这个最少意味着反转 L',其中 L' 是 L' 是没有第一个元素的列表(通常称为
head
),而不是添加head 作为列表的最后一个元素。 Return 值将与您刚刚进行的递归调用的 return 值相同。
可以做到。理解递归的关键是起点是什么?
通常我会创建一个 "starting" 函数来准备第一次调用。有时它是一个单独的函数(比如在底部的非 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
然后函数调用:
a.Reverse(nullptr)
被调用。- 这会在父节点
a
的下一个节点b.Reverse
上调用Reverse
。 - 这会在父节点
b
的下一个节点c.Reverse
上调用Reverse
。 - 这会在父节点
c
的下一个节点d.Reverse
上调用Reverse
。 d
没有next
节点所以它说新头就是它自己。d
的next
现在是父级c
d
returns 本身就是new_head
.- 返回
c
:从d
返回的new_head
是d
c
的next
现在是父级b
c
returns 从d
收到的 - 返回
b
:从c
返回的new_head
是d
b
的next
现在是父级a
b
returns 从c
收到的 - 返回
a
:从b
返回的new_head
是d
a
的next
现在是父级nullptr
a
returns 从b
收到的 d
返回
new_head
new_head
new_head
非面向对象的实现;
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;
}