注意链表进行翻转等操作时,由于链表最后一个结点指向空,实际要修改n条边而不是n-1条。
由于链表只能从前向后进行访问,在反转时要将前一个节点改为后一个结点时,必须要存储前一个节点
head指向链表的头一个节点,head->val=1
while(head->next!=NULL){
printf("pre%d\n",head->val);
head=head->next;
printf("after%d\n",head->val);
}
while(head!=null)
访问了n个点
1 2 3 4 5
while(head->next!=null)
pre访问了n-1个点
1 2 3 4
after访问了n-1个点
2 3 4 5
法一:迭代
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *last=NULL;
while(head!=NULL){
//由于head指针的指向改变,导致head->next改变,所以提前保存head->next
ListNode *nextNode=head->next;
//改一条边
head->next=last;
//last和head都向后移一位
last=head;
head=nextNode;
}
//当head=NULL时,循环结束,此时last指向NULL的前一个节点,是原链表的最后一个结点,为反转链表的头结点
//返回反转链表的头结点即可
return last;
}
};
法二:递归
为什么可以用递归?
符合递归的三个条件
①可以将大问题拆解成两个子问题
都可以将问题求解划分为头节点的反转和除了头结点剩余所有节点的反转
②子问题求解方式和大问题一样
③存在最小子问题
最后head=NULL时不需要反转
解法:
先递后归
递到最小问题后逐层向上求解
每一步都是对头结点进行
head.next.next=head
head.next=NULL
操作
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//递归终止条件
//注意head==NULL要写在head->next==NULL前面
//不然当head=NULL时,head->next访问不到,就会报错
if(head==NULL||head->next==NULL)return head;
//先递,返回反转链表头结点
ListNode *p=reverseList(head->next);
//后归,对头结点进行操作
head->next->next=head;
head->next=NULL;
return p;
}
};