题目
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
解法一:三指针迭代法
反转链表最大的问题在于当该结点的next指向前一个结点后它的后一个结点就找不到了。为了能够找到前一个结点,我们可以再引入一个变量p,在next改变之前就往后移动,这样就可以通过p节点找到该节点的下一个结点了。
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null; //该节点的前一个
ListNode cur = head; //该节点
if(head == null){
return null;
}
while(cur.next != null){
ListNode n = cur.next; //该节点的后一个
cur.next = pre;
pre = cur;
cur = n;
n = n.next;
}
cur.next = pre;
return cur;
}
}
解法二:栈存储法
提到倒置,我们可以联想到数据结构栈。栈的放入取出就是一次顺序的倒置。
class Solution {
public ListNode reverseList(ListNode head) {
Stack<ListNode> s = new Stack<ListNode>();
if(head == null){
return null;
}
while(head.next != null){
s.push(head);
head = head.next;
}
s.push(head); //还有最后一个没入栈
ListNode newHead = new ListNode();
ListNode p = newHead;
while(!s.isEmpty()){
ListNode n = s.pop(); //结点需要新建,而不能直接对取出的结点进行连接
p.next = n;
p = n;
}
p.next = null; //将最后一个结点的next置空
return newHead.next;
}
}
注意:一定记得将最后一个结点的next置空, 否则由于它的next本身指向的是前一个结点,就会倒置最后两个结点互指,从而导致循环
解法三:头插法
我们知道链表有两种插入结点的方法,头插和尾插。当我们把链表从头开始遍历,然后新建链表进行头插操作,不就可以实现链表的倒置了吗?
class Solution {
public ListNode reverseList(ListNode head) {
ListNode newHead = new ListNode();
while(head != null){
ListNode p = new ListNode(); //每次创建一个结点
p = head; //让其与原链表的头指针所指结点相等
head = head.next; //原链表头指针移动
p.next = newHead.next;
newHead.next = p; //这两行为头插
}
return newHead.next;
}
注意:head = head.next的位置必须在头插之前,否则由于p指针指向的就是head结点,头插会直接改变head的next,导致head = head.next失效。
解法四:递归
递归的适用情况需满足一下三点:
- 大问题可以拆成两个子问题
- 子问题的求解方式和大问题一样
- 存在最小子问题
对于反转链表我们可以将其拆解为反转头节点和剩下的结点。
- 头节点怎么反转?就是头节点的下一个结点的next指向头节点,头节点的next置空。
- 其它结点和头节点的求解方式一样吗?当然一样了,都是该节点和后一个结点的next转换
- 存在最小子问题吗?存在,当没有下一个结点时
那么我们就可以使用递归方法来做,返回新的头节点。
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode newNode = reverseList(head.next);
head.next.next = head;
head.next = null;
return newNode;
}
}