递归法的核心思想:利用递归的层级机制,下探到最低级别进行逆序,递归返回时层层逆序,最后到头节点时再逆序一次,逆序完成。
假设有链表A——>B——>C——>D——>nullptr。
递归到最低级别时开处理C,逆序CD,变为A——>B——>C<——D
处理B,逆序BC,变为A——>B<——C<——D
处理A,逆序AB,变为nullptr<——A<——B<——C<——D
到这里,把D当成新链表的头节点就行了。
这个流程中有个奇怪的地方,链表的最后一个节点明明是D,为什么从C开始逆序处理?网上很多文章和代码都没给出这点解释,也许因为原因很简单?既然是逆序,就必须要有两个节点才能做到,比如AB或者CD,而最后一个D无法和任何节点逆序,因为是单链表,所以D不知道有其他节点的存在,故逆序只能从C开始,C和D逆序。
OK,思想和流程都清楚了,接下来进行细节的处理。
递归的终止条件是什么?返回值是什么?
ListNode* reverseList(ListNode* head)
{
if(what?)
return what?;
}
可以看出,递归最后返回的一定是最后的D节点,把D节点当做函数返回的头节点,层层返回的节点也都是D,这样最后函数的返回值才是正确的。
终止条件很简单,只要最后到达D节点了,我们就把它返回,于是有如下代码
ListNode* reverseList(ListNode* head)
{
//非法检测
if(head == nullptr)
return nullptr;
//最后的D节点了
if(head->next == nullptr)
return head;
}
这样判断代码看起来逻辑很清楚,为使得代码更精简,很多网友会把代码等价转换为如下形式
ListNode* reverseList(ListNode* head)
{
if ((head == nullptr) || (head->next== nullptr))
return head;
}
终止条件和返回值有了,如何进行当前级别的逆序?
//逆序CD很简单
D->next = C;
回到具体的函数中,head就是所谓的C(为什么不能是D?因为如果是D,函数的前两行代码就返回了)C有了,D在哪?D就是C->next,所以代码变为
//逆序C和后续节点很简单
C->next->next = C;
再把C替换为head
//逆序head和后续节点很简单
head->next->next = head;
这样解释,这行代码是不是就太简单了!很多网友在解释递归法时一通代码扔出来,一上来就是head->next->next=head,让人看得云里雾里!这行代码的意思就是逆序当前节点和它的后续节点。
到此,函数代码大致变为
ListNode* reverseList(ListNode* head)
{
if ((head == nullptr) || (head->next == nullptr))
return head;
...
head->next->next = head;
...
}
当前层级的逆序必须在下层逆序完成之后,所以代码继续衍变为
ListNode* reverseList(ListNode* head)
{
if ((head == nullptr) || (head->next == nullptr))
return head;
//先逆序低级别的,lastNode就是最终的D节点
ListNode* lastNode = reverseList(head->next);
head->next->next = head;
//始终返回D节点
return lastNode;
}
至此似乎工作已经完成,我们检查一下
最后处理完A节点变为 A<——B<——C<——D,这里还有一个关键点,AB逆序后,B->next = A,那A的next是谁?如果完美逆序,A的next应该是nullptr,所以问题变为在什么地方把A的next设置为nullptr?再仔细分析一下,当我们一开始逆序CD后,D->next = C,要想设置C的next必须等待递归返回处理BC的时候再设置C->next = B,那么其实如果我们在逆序CD时设置C->next = nullptr好像并不影响B的处理,而且这也解决了A的next问题,即处理AB时,B->next = A,A->next = nullptr(因为A已经是最上层的了,没有OA逆序了,所以不存在A->next = O这种过程,最终A的next就是nullptr),于是代码继续衍变为
ListNode* reverseList(ListNode* head)
{
if ((head == nullptr) || (head->next == nullptr))
return head;
//先逆序低级别的,lastNode就是最终的D节点
ListNode* lastNode = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
//始终返回D节点
return lastNode;
}
至此,链表逆序的工作正式完成。