算法村里着重强调了反转链表的重要性。
那么为什么它重要呢?
因为反转链表涉及结点的增加、删除等多种操作,能非常有效考察思维能力和代码驾驭能力。另外很多题目也都要用它来做基础, 例如指定区间反转、链表K个一组翻转。还有一些在内部的某个过程用到了反转,例如两个链表生成相加链表。还有一种是链表排序的,也是需要移动元素之间的指针,难度与此差不多。
——算法村
先来看反转链表的题目
一、LeetCode206.反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
一)方法一:迭代
官方题解用的是迭代的方法。
解题思路:遍历原链表,通过将当前节点插入到新链表的头部,实现链表的反转。
代码如下
class Solution {
public ListNode reverseList(ListNode head) {
// 定义两个指针,prev用于指向当前节点的前一个节点,curr用于指向当前节点
ListNode prev = null;
ListNode curr = head;
// 遍历链表,直到当前节点为空
while (curr != null) {
// 临时保存下一个节点的指针
ListNode next = curr.next;
// 将当前节点的next指针指向前一个节点,实现反转
curr.next = prev;
// 更新prev和curr指针,prev指向当前节点,curr指向下一个节点
prev = curr;
curr = next;
}
// 返回反转后的头节点
return prev;
}
}
看了解题思路后,我自己独立写了一遍,但是最后的 return prev这儿写成了 return curr.
当时我还不知道错哪了,想半天,就是应该返回 curr,它不就是当前的头结点吗。
可能有小伙伴跟我一样是这么觉得,实际上上面 prev = curr; curr = next;
这里就使得prev变成了当前的头结点,所以就应该返回 prev.
二)方法二:递归
这个方法有点难,仅做参考。
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;
}
}
解题思路:通过不断将当前节点连接到已反转子链表的尾部来实现链表的反转。