每K个一组反转链表

x33g5p2x  于2021-12-31 转载在 其他  
字(2.2k)|赞(0)|评价(0)|浏览(229)

前言

上一章连接【链表反转问题】
上一章已经讲过了单向链表的反转,接下来我们在上一章的基础上来一道进阶题:
每K个一组反转链表

问题描述

问题连接:【每K个一组反转链表】
描述
给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序。

说明:

  1. 你需要自行定义链表结构,将输入的数据保存到你的链表中
  2. 不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换
  3. 你的算法只能使用常数的额外空间

输入描述:
第一行输入是链表的值
第二行输入是K的值,K是大于或等于1的整数

输入形式为:
1 2 3 4 5
2

输出描述:
当 k = 2 时,应当输出:
2 1 4 3 5

当 k = 3 时,应当输出:
3 2 1 4 5

当k=6时,应当输出:
1 2 3 4 5

示例1
输入:
1 2 3 4 5
2
输出:
2 1 4 3 5

解题思路

我们上一章做的是 两个两个交换 ,而这道题是k个k个交换,其实都是差不多的
解这道题的大致思路为:

  1. 选出前k个节点
  2. 先反转前k个节点组成的链表
  3. 剩余节点组成的链表,我们可以看成一个新的链表,
  4. 就可以将“反转剩余节点的链表”看成一个的子问题

说到子问题相信大家都知道要用递归求解了

在上代码之前,希望大家有上一章反转单链表的基础,不然代码可能不是太好理解。传送门->【链表反转问题】

代码实现

  1. import java.util.*;
  2. class ListNode{
  3. int val;
  4. ListNode next = null;
  5. ListNode(int val){
  6. this.val = val;
  7. }
  8. }
  9. public class Main{
  10. public static void main(String[] args){
  11. Scanner sc = new Scanner(System.in);
  12. // 接收输入的第一行数据
  13. String str = sc.nextLine();
  14. // 以 一个空格 为界 分开这些数字
  15. String[] strArr = str.split(" ");
  16. // 创建链表的头结点
  17. ListNode head = new ListNode(-1);
  18. ListNode rear = head; // rear为节点
  19. // 将数据都插入到单向链表中
  20. for(String item: strArr) {
  21. ListNode newNode = new ListNode(Integer.parseInt(item));
  22. // 插入节点 (尾插法)
  23. rear.next = newNode;
  24. rear = newNode;
  25. }
  26. // 接收输入的k
  27. int k = sc.nextInt();
  28. // 调用我们的反转方法
  29. ListNode newHead = reverse(head.next, k);
  30. // 输出反转后的链表
  31. ListNode cur = newHead;
  32. while(cur != null) {
  33. System.out.print(cur.val+" ");
  34. cur = cur.next;
  35. }
  36. }
  37. /** * 以k个节点为一组 分组反转单向链表 */
  38. public static ListNode reverse(ListNode head, int k){
  39. // 特殊情况一:【链表为null 或者 k<=1, 没必要反转】
  40. if(head == null || k <= 1) {
  41. return head;
  42. }
  43. // cur 指向当前节点
  44. ListNode cur = head;
  45. // 下面这一段代码主要用来判断, 链表长度是否小于k
  46. for(int i=1; i<k && cur!=null; i++) {// 注意这里i初始值为1, 条件是i<k,相当于cur后移了k-1步
  47. // cur后移
  48. cur = cur.next;
  49. }
  50. // 特殊情况二 :【链表剩余长度不足k,不用反转】
  51. // 如果cur为null 则说明上面跳出for循环的原因是: 链表的长度不足k,题目要求不用反转,直接返回head即可
  52. if(cur == null) {
  53. return head;
  54. }
  55. // 排除完特殊情况 下面就是正常情况了。 此时cur已经指向第k个节点了
  56. // 先保存下一节点(k+1)地址 , 因为接下来要断开 cur与k+1 节点的连接
  57. ListNode nextListHead = cur.next;
  58. // 断开k节点 与 k+1节点的连接 因为接下来要单独反转当前组单链表了
  59. cur.next = null;
  60. // 反转当前组 单项链表
  61. ListNode newHead = reverse(head);
  62. // 递归处理后面链表
  63. ListNode newNextHead = reverse(nextListHead,k);
  64. // 重新连接之前断开的链表
  65. head.next = newNextHead; // 这里的head 其实已经被反转到后面了,现在是第k个节点
  66. // 返回反转后的新头节点
  67. return newHead;
  68. }
  69. /** * 反转整个单向链表 */
  70. public static ListNode reverse(ListNode head) {
  71. ListNode pre = null;
  72. ListNode cur = head;
  73. ListNode next_ = null;
  74. while(cur != null) {
  75. // next_指向当前节点的下一个节点
  76. next_ = cur.next;
  77. // 开始局部反转 当前节点的next域指向前一节点
  78. cur.next = pre;
  79. // pre指针后移
  80. pre = cur;
  81. // cur指针后移
  82. cur = next_;
  83. }
  84. return pre;
  85. }
  86. }

在代码中每一步我都有详细的注解,如果你有耐心看完的话肯定会理解的。(一遍没看明白可以多看几遍)

相关文章