题目描述:
给定一个单链表的头节点head,将链表反转,并将所得结果返回。
示例1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
图示如下:
示例2:
输入:head = [1,2] 输出:[2,1]
图示如下:
示例3:
输入:head = [] 输出:[]
解法一:双指针迭代法。
思路:根据单链表的特点,对于任一节点而言,仅指向它的直接后继节点。因此,要想反转链表,就需要改变每个节点的next指向。如果仅使用一个变量进行链表反转是达不到预期目标的,以示例1为例,假设让节点2的next指向了节点1,那么就无从获知原来链表中节点2的下一节点信息,因为节点2的next指向已经变为节点1。所以,在改变节点2的next之前,应该对节点2的下一个节点使用变量进行标记。具体如下图所示:
因此,可将反转链表分为两种情况:
(1)特殊情况:链表为空或链表只有一个节点,无需反转,直接返回head即可。
(2)一般情况:如上图所示,cur代表循环变量,temp用于标记cur的next值,pre代表cur的前一==节点。当且仅当cur值为null时,遍历结束。
代码:
class Solution {
public ListNode reverseList(ListNode head) {
// 处理特殊情况,链表为空和只有一个节点的链表
if(head == null || head.next == null){
return head;
}
//每次遍历,标记当前节点的下一个节点,改变当前节点的next指向,并将当前节点和前一节点后移
ListNode cur = head, pre=null, temp;
while(cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
解法二:递归法。
思路:递归的思想在于将一个大问题细化为具有相同性质的小问题。以示例1为例,可以将反转整个链表的操作划分为四个子问题。因此,研究出一个子问题的解决方案,之后推广到整个链表即可。
以节点1和节点2的反转过程为例,在初始条件下传入head,此时head指向的是节点1,而节点2则为head.next来表示。因此,要对这两个节点进行反转,只需要两个步骤:
(1)节点2的next指向节点1,即:head.next.next = head;
(2)节点1的next置为null,即:head.next = null;
如下图:
因此,推广到整个链表。对于整个链表来讲,为了防止节点2反转之后,与节点2之间不能断开,故而需要在反转节点之间对节点2的next进行存储,如同解法1中一般。这里借助递归的特性,即:递归类似于栈的功能,在反转结点之前先调用方法。如下图:
代码:
class Solution {
public ListNode reverseList(ListNode head) {
// 处理特殊情况,链表为空和只有一个节点的链表
if(head == null || head.next == null){
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}