题目
定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
思考题:
请同时实现迭代版本和递归版本。
数据范围
链表长度 [0,30]。
样例
输入:1->2->3->4->5->NULL
输出:5->4->3->2->1->NULL
迭代版代码一
从第一个节点和第二个节点开始遍历到尾节点的后一个节点,
最开始时前驱指针指向链表的第一个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head || !head->next) return head;//如果链表为空或者只有一个节点,则不需要反转
ListNode* a = head, *b = head->next;//a为第一个节点,b为第二个节点
while(b){//等价于a->next
auto c = b->next;//临时保存b的下一个节点
b->next = a;//b的next指针指向a
a = b, b = c;//a和b往后移动一个节点
}
head->next = NULL;//给反转后的尾节点的next指针置为NULL;
return a;//a此时为反转后的头节点
}
};
迭代版代码二
从头节点开始遍历到尾节点,
最开始时前驱指针指向为null
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode * pre = nullptr;
ListNode * cur = head;
while(cur){//当cur遍历到链表尾
auto next = cur->next;//临时保存cur的下一个节点地址
cur->next = pre;//更新当前节点的指向
pre = cur;//前驱节点移动到当前节点
cur = next;//当前节点移动到cur的下一个节点
}
return pre;//返回原链表尾节点的前驱节点,即反转链表后的头节点
}
};
递归版代码
- 关键是利用递归结束条件,控制最深层递归是在尾节点return尾节点地址
- 因为递归的参数为
head->next
,所以当即5->next==null
满足递归结束条件head->next == null
退出到上一层递归时,,head为尾节点5的前驱节点4- 再执行两行修改节点指针的关键代码
head->next->next = head;
目的是通过访问head节点4,修改节点4的后继节点5的next指针指向节点4head->next = null;
目的是让当前head节点4指向空- 因为前面调用递归函数的返回值一直是最深层递归退出时的
head->next==null
,即原链表的尾节点,一直没有发生变化
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head || !head->next)return head;//如果链表为空,或者满足递归结束条件head->next==null
auto res = reverseList(head->next);//保存返回最终链表的头节点,返回值一直都是最终链表的头节点
head->next->next = head;//当最深一层递归退出时,即5->next == null时退出,递归结束条件直接返回head,即res = head==5
//此时返回到上一层的递归时的head为4,此行代码的目的是让5的next指针指向4
head->next = nullptr;//让当前节点指向空
return res;//返回一直保存的最终链表的头节点,即head==5
}
};