前言:
本文先从链表的翻转算法和链表两两交换的算法讲起,最后糅合起来讲解K个一组的链表翻转算法。
1、206. 反转链表
问题描述:
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
题解:
1、利用外部空间:
这种方式很简单,先申请一个动态扩容的数组或者容器,比如 ArrayList 这样的。然后不断遍历链表,将链表中的元素添加到这个容器中。再利用容器自身的 API,反转整个容器,这样就达到反转的效果了。最后同时遍历容器和链表,将链表中的值改为容器中的值。因为此时容器的值是:5 4 3 2 1。链表按这个顺序重新被设置一边,就达到要求啦。
当然你可以可以再新创建 N 个节点,然后再返回,这样也可以达到目的。
这种方式很简单,但你在面试中这么做的话,面试官 100% 会追问是否有更优的方式,比如不用外部空间。很简单所以我就不做代码演示了,下面来看看如何用 O(1)O(1) 空间复杂度来实现这道题。
2、双指针迭代
我们可以申请两个指针,第一个指针叫 pre,最初是指向 null 的。
第二个指针 cur 指向 head,然后不断遍历 cur。
每次迭代到 cur,都将 cur 的 next 指向 pre,然后 pre 和 cur 前进一位。
都迭代完了(cur 变成 null 了),pre 就是最后一个节点了。
动画演示如下:
动画演示中其实省略了一个tmp变量,这个tmp变量会将cur的下一个节点保存起来,考虑到一张动画放太多变量会很混乱,所以我就没加了,具体详细执行过程,请点击下面的幻灯片查看。
代码实现:
class Solution {
public ListNode reverseList(ListNode head) {
//申请节点,pre和 cur,pre指向null
ListNode pre = null;
ListNode cur = head;
ListNode tmp = null;
while(cur!=null) {
//记录当前节点的下一个节点
tmp = cur.next;
//然后将当前节点指向pre
cur.next = pre;
//pre和cur节点都前进一位
pre = cur;
cur = tmp;
}
return pre;
}
}
3、递归解法
这题有个很骚气的递归解法,递归解法很不好理解,这里最好配合代码和动画一起理解。
递归的两个条件:
1、终止条件是当前节点或者下一个节点==null
2、在函数内部,改变节点的指向,也就是 head 的下一个节点指向 head 递归函数那句