递归之24&206(链表)
Given a linked list, swap every two adjacent nodes and return its head.
You may not modify the values in the list's nodes, only nodes itself may be changed.
Example:
Given 1->2->3->4, you should return the list as 2->1->4->3.
Reverse a singly linked list.
Example:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
Follow up:
A linked list can be reversed either iteratively or recursively. Could you implement both?
这里我们只考虑递归算法,24是从头开始,两两节点作交换。206是从头到尾逆序。(只考虑交换节点而非新建链表或交换节点值的实现)
我们上代码
24
1 class Solution { 2 public: 3 ListNode* swapPairs(ListNode* head) { 4 if (head==NULL || head->next==NULL) 5 return head; 6 ListNode *t = head->next; 7 head->next = swapPairs(t->next); 8 t->next = head; 9 return t; 10 } 11 };
206
1 class Solution { 2 public: 3 ListNode* reverseList(ListNode* head) { 4 if (head == NULL || head->next == NULL) { 5 return head; 6 } 7 ListNode* ret = reverseList(head->next); 8 head->next->next = head; 9 head->next = NULL; 10 return ret; 11 } 12 };
均以1->2->3->4链为例:
(格子里面的数字为对应代码段的首行代码号)
(*3 代表指向值为3的节点的指针)
(双箭头线为程序执行流)
考虑24的时候,可见三个节点-> 当前节点,当前节点指向的下一个节点,当前节点指向的下一个节点的下一个节点(递归返回)。
交换这三个节点的前两个节点即可,把递归返回节点视为已调整好的节点。(注意递归调用在代码段中央,这个中央是指节点指针域修改中)
而考虑206时,由于是要整个交换链表,那么宏观上就得有个概念,最终返回的是原链表最后一个节点的指针,所以执行时不断压入当前节点的下一个节点的指针,而返回的始终是“相对原链表的最后一个节点的指针”。(注意递归调用在代码段前,即在节点指针域修改前,因而能实现直接压到最后一个节点,而程序不断返回上一次递归的返回值,即从首次退栈<head=*4时首次退栈>到结束都是返回同一个指针)
递归执行中出现了某节点前后节点均指向自身的情况,前节点指向自身是前节点还未修改指针域,后节点指向自身是因为递归返回,上一次递归时“该节点的下一个节点”的指针域修改指向当前节点。递归结束后返回尾节点指针(现为头节点)
看程序和写程序是两码事,现在瞟一眼别人的指针个数和位置大概能明白和写出来。但是完全凭自己写还是太困难。
1.返回什么
2.干什么
3.什么时候退出
->4.递归调用位置。
每个人考虑问题的方式不同,造成每个选项可能都有异。从不同问题规模建模,每个选项干的事可能都不同,可能产生很多条路,但是通往正确的道路确实有限的。