1.思路分析

反转链表的前k个结点其实与反转链表差别不大,关键点在于我们如何提前结束反转链表,在反转链表中,我们的结束条件是什么呢?head.next == null,欧克,现在我们只要修改这个条件就能达到提前结束反转链表,只反转链表的前n个结点。

2.具体分析

a.传入第k+1个结点

在反转链表中,我们这里的k就是整个链表的长度,也就是说,其实反转链表是反转链表前n个结点的特殊情况罢了。那现在我们就要从特殊分析一般的情况,

反转结点数 结束条件
反转链表 n(链表的长度) head.next == null
反转链表前k个结点 k(k<=n) ?

?代表的是什么条件,让我们来想想看,n个时,head.next指向null为终止,null所在的位置不就是第n个结点的后一个吗?那一切就明白了,通用的结束条件应该是head.next == ‘第k+1个结点’,有了这个我们就能写出代码了。

  1. public ListNode reverseK(ListNode head, ListNode kplusOne) {
  2. // head.next == null才是base case ,head == null是为简洁而合并的
  3. if(head == null||head.next == kplusOne) return head;
  4. //递归公式
  5. ListNode last = reverseList(head.next);
  6. head.next.next = head;
  7. head.next = null;
  8. //转换head
  9. return last;
  10. }

哎?这样写真的正确吗?

反转前k个.jpg

通过上面这过程,我们突然发现了问题,head.next = null后,第k个结点后面的部分不就都丢失了吗?要怎么更改代码呢?让我们想想反转整个链表的时候,为什么要指向null,你可能会说,因为后面没有节点了啊,肯定指向null,不然还能指向啥?那再想想,如果后面有结点呢?那肯定指向后面的结点,对吧,这下我们就分析清楚啦!此时后面有结点我们不能指向null,要指向后面不反转的结点。

  1. public ListNode reverseK(ListNode head, ListNode kplusOne) {
  2. // head.next == null才是base case ,head == null是为简洁而合并的
  3. if(head == null||head.next == kplusOne) return head;
  4. //递归公式
  5. ListNode last = reverseList(head.next);
  6. //保留不反转的部分
  7. ListNode rest = head.next.next;
  8. head.next.next = head;
  9. //指向不反转的部分
  10. head.next = rest;
  11. //转换head
  12. return last;
  13. }

欧克,这下就正确了,但是还没有完,你要问了,题目只给了你k这个参数,该怎么办呢?

b.传入K

这并不难,我前面说过,只要找到结束条件就可以了。

反转k个_2.jpg

我们使用递归就是因为可以把原问题分解为相同的子问题,原问题可以由子问题一层一层往上解决,而最终解决。我们是求前K个结点,当往下的时候,就是求下一个链表的前K-1个结点,直到K==1的时候,我们结束了,因为反转前1个结点就等于没有反转,这其实也是递归反转链表的核心思想。

  1. public ListNode reverseK(ListNode head, int k) {
  2. if(k == 1) return head;
  3. //递归公式
  4. ListNode last = reverseList(head.next, k-1);
  5. //保留不反转的部分
  6. ListNode rest = head.next.next;
  7. head.next.next = head;
  8. //指向不反转的部分
  9. head.next = rest;
  10. //转换head
  11. return last;
  12. }