题目描述
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
思路分析
「原地逆置」
利用三指针 pre、cur 和 next 三个连续的指针,分别指向前一个节点、当前节点以及当前节点的下一个节点。
在原地进行逆置,cur.next = pre,那么 cur 就会和后面的节点开连接,为了避免这一问题,需要使用 next 指针提前保存 cur 后面的节点。
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode temp = null;
while(cur != null) {
temp = cur.next; // 记录当前节点的下一个节点
cur.next = pre; // 当前节点指向前一个节点
pre = cur; // pre指针后移
cur = temp; // cur指针后移
}
return pre;
}
}
「头插法」
构建一个哑节点,并同时遍历链表,将遍历到的节点插入到哑节点之后。
class Solution {
public ListNode reverseList(ListNode head) {
// 构建哑节点
ListNode dummy = new ListNode(0);
ListNode node = head;
ListNode temp = null;
while (node != null) {
// 保存下一个节点
temp = node.next;
// 插入节点头部
node.next = dummy.next;
dummy.next = node;
// node指针后移
node = temp;
}
return dummy.next;
}
}
「栈的使用」
可以利用栈将全部节点都压入栈中,取数据时,链表后面的节点自然就被放在了栈顶,重新进行指针的指向即可。不过这里需要注意的是,出栈时,每个节点的 next 指针都指向了新的节点,唯独头节点的指针是我们的处理盲区,因此我们需要进行 head.next = null 的操作,否则将会出现循环链表。
class Solution {
public ListNode reverseList(ListNode head) {
Stack<ListNode> stack = new Stack<>();
while (head != null) {
stack.push(head);
head = head.next;
}
ListNode dummy = new ListNode(0);
ListNode temp = dummy;
while (!stack.empty()) {
temp.next = stack.pop();
temp = temp.next;
}
temp.next = null;
return dummy.next;
}
}
「递归操作一」
利用递归进行链表操作,递归中需要做的事情必然是获得当前节点的下一个节点 next,然后将 next 节点的指针指向当前节点 cur。
因此,递归的返回条件也就很明显了,只要当前节点的下一个节点为空,即 cur.next == null,就可以直接返回了,返回当前节点。
这样就可以不断进行递归了,直到所有的指针都逆置,指向它的前一个节点。
private ListNode reverse(ListNode cur) {
if (cur.next == null) return cur;
ListNode nex = reverse(cur.next);
nex.next = cur;
return cur;
}
当然这样子还不够,虽然所有指针都被逆置了,但是我们可没法获得逆置后的头节点,因此,我们需要有一个辅助节点来帮助我们获得头节点。
ListNode node = null;
private ListNode reverse(ListNode cur) {
if (cur.next == null) {
node = cur;
return cur;
}
ListNode nex = reverse(cur.next);
nex.next = cur;
return cur;
}
另外,这里也需要对头节点进行处理,避免循环链表。完整的代码如下:
class Solution {
ListNode node = null;
public ListNode reverseList(ListNode head) {
if (head == null) return node;
head = reverse(head);
head.next = null;
return node;
}
private ListNode reverse(ListNode cur) {
if (cur.next == null) {
node = cur;
return cur;
}
ListNode nex = reverse(cur.next);
nex.next = cur;
return cur;
}
}
「递归操作二」
前面的代码中我们遇到的一个问题是无法获得逆置后链表的头节点,因此我们需要使用一个辅助节点来帮助我们保存头节点。
那能不能不使用这个辅助节点,直接通过递归来返回逆置后的头节点呢?答案是可以的。
既然我们所返回的是逆置后的头节点,那就返回不了当前节点了,那该如何逆置呢?很显然 cur.next.next = cur。
完整代码如下,同样的,这里也需要对头节点进行处理,避免循环链表。
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) return null;
ListNode tail = reverse(head);
head.next = null;
return tail;
}
private ListNode reverse(ListNode cur) {
if (cur.next == null) return cur;
ListNode tail = reverse(cur.next);
cur.next.next = cur;
return tail;
}
}