今天在工作中,我一直在试图理解这个问题。我发现您的限制有些莫名其妙,因此“您尝试过魔法吗?”评论。最终我克服了障碍......
这可能有助于可视化问题。让我们从 Julio Moreno 的代码中的想法开始,稍微简化一下:遍历列表并将每个节点数据与尾部数据交换。
A B C D E
E B C D A
E A C D B
E A B D C
E A B C D
E D B C A
E D A C B
E D A B C
E D C B A
(在工作中我断定这个过程不会成功,但现在我有更多时间可以看到它了)。如果我们再仔细看看他的函数,我们可以看到除此之外,它还可以递归地工作。我并不热衷于可视化从 for 循环调用的递归函数。这个过程显然不会因效率而赢得任何奖励。
那么让我们看看我们可以做什么如果我们不想限制自己不修改节点位置:
A B C D E
B C D E A
C D E B A
D E C B A
E D C B A
这里我们取尾节点 E,并记住它。我们现在将节点 A 插入到 E 之后,然后将 B 插入到 E 之后但在 A 之前,逐步遍历整个列表,立即插入 E 之后,直到 E 成为列表的第一个节点(头)。它有效,但我们不允许这样做。
让我们假装更进一步,假设它是一个双向链表,我们维护两个指针,一个在列表的开头,一个在结尾,我们交换两者的数据,然后递增一个和分别递减另一个。
A B C D E
E B C D A
E D C B A
已经完成了!
那么我们如何使用单链表来做到这一点呢?我们需要知道什么?我们如何在退步的同时又前进?
让我们从如何通过遍历整个列表来获取最后一个节点开始。
A B C D E F G H
然后交换它们:
H B C D E F G A
然后如果我们记得我们交换了数据的两个节点,我们可以从 B 开始并逐步前进,直到 node->next 指向现在保存数据 A 的节点。
B C D E F G
并交换它们:
G C D E F B
F D E C
E D
但是,我仍然对重复遍历列表的想法感到不舒服 - 即使步进的范围在每次迭代过程中都会缩小。如果我们有一个 LIFO(后进先出)或其他已知的堆栈怎么办?
A B C D E F G H
B C D E F G H ... A
C D E F G H ... B
D E F G H ... C
E F G H ... D
F G H ... E
G H ... F
H ... G...F...E...D...C...B...A
但这是辅助数据存储,我们不允许这样做,但不难看出如何使用递归函数调用和链表来实现 LIFO。那么我们如何通过递归函数调用和链表来前进和后退呢?我们不需要额外的参数吗?当我们到达列表的末尾时,我们仍然需要知道它是如何开始的。
A B C D E F G H
A,B ^ return 0
A,C ^ return 0
A,D ^ return 0
A,E ^ swap D E done, return 0
A,F ^ swap C F return D
A,G ^ swap B G return C
A,H ^ swap A H return B
我还没有实际测试过这一点来证明它,所以它可能是错误的。我现在就去测试它,如果需要可以发布代码。希望我不必编辑这篇文章就说它不起作用;-)
编辑:可以确认它有效。
static lnode* list_private_reverse(lnode* list, lnode* node)
{
lnode* next = node->next;
if (next)
{
lnode* swap = list_private_reverse(list, next);
if (swap)
{
int c = swap->c;
swap->c = node->c;
node->c = c;
if (swap->next == node || swap->next->next == node)
return 0;
return swap->next;
}
return 0;
}
else
{
int c = node->c;
node->c = list->c;
list->c = c;
}
return list->next;
}
lnode* list_reverse(lnode* list)
{
list_private_reverse(list, list);
return list;
}
list_private_reverse 的调用次数与列表中的元素数量相同。