单链表中k个结点一组进行反转

本文介绍了一种链表的处理方法,即按K个元素为一组进行反转。通过递归和非递归两种方法详细阐述了算法的实现过程,并提供了具体的Java代码示例。

原题

  Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
  If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.
  You may not alter the values in the nodes, only nodes itself may be changed.
  Only constant memory is allowed.
  For example,
  Given this linked list: 1->2->3->4->5
  For k = 2, you should return: 2->1->4->3->5
  For k = 3, you should return: 3->2->1->4->5

题目大意

  给定一个单链表,和一个分组数K,每K个结点进行反转,如果最后的结点数不足K个就保持原来的链接顺序不变。

解题思路

  用一个指针记录链接好的链表的最后一个结点(tail),用一个指针记录上一次链接好的部分的最后一个结点(head)对未链表的结点在head处进行尾插法,插k个元素,再将head移动到tail处,tail重新记录链表的尾结点,直到所有的元素都进行了操作。如果最后的一组元素不足k个,因为进行过尾插法,所以还要进行还原,对最后的head元素进行尾插法就可以了

代码实现

结点类

public class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

算法实现类

public class Solution {

    public ListNode reverseKGroup(ListNode head, int k) {

        if (k <= 1) {
            return head;
        }

        ListNode root = new ListNode(0);
        // 分组的头一个元素的前驱
        ListNode groupHead = root;
        // 当前要处理的结点
        ListNode curr = head;
        // 处理好的链表的尾结点
        ListNode groupTail = head;
        // 当前要处理的结点的后继
        ListNode next;

        // 对每个组,处理了多少个结点
        int count = 0;


        while (curr != null) {

            // 如果是分组的第一个元素就记录它
            if (count == 0) {
                groupTail = curr;
            }

            // 记录处理的元素个数
            count++;
            // 记录下一个待处理结点
            next = curr.next;
            // 进行尾插法操作
            curr.next = groupHead.next;
            groupHead.next = curr;
            curr = next;

            // 已经处理完了k个结点,分组头的前驱移动到最后一个链接好的结点
            if (count == k) {
                groupHead = groupTail;
                // 计数器归零
                count = 0;
            }
        }


        // 说明结点个数不是k的整数倍,将最后不是整数倍的个元素的结点,
        // 再次使用尾插法进行还原
        if (count != 0) {
            curr = groupHead.next;
            groupHead.next = null;

            while (curr != null) {
                next = curr.next;
                curr.next = groupHead.next;
                groupHead.next = curr;
                curr = next;
            }
        }

        return root.next;
    }
}

 

 

问题:

以k个元素为一组,反转单向链表。比如:

输入: 1->2->3->4->5->6->7->8->null and k = 3

输出:3->2->1->6->5->4->8->7->null. 

分析:

我们可以把整个链表分成多个长度为 k  的子链表, 然后,我们再反转每一个子链表(递归)。问题的关键是我们需要把每个子链表再连接起来。所以,对每一个子链表操作以后,我们需要返回该子链表的头(head),然后,我们设置前一个子链表的最后一个node,把它的next 设置成下一个链表返回的头(head),这样,所有的子链表就连接起来了。

 

[java] view plain copy

 

  1. public static Node reverse (Node head, int k) {  
  2.     Node current = head;  
  3.     Node next = null;  
  4.     Node prev = null;  
  5.     int count = 0;     
  6.       
  7.     /*reverse first k nodes of the linked list */  
  8.     while (current != null && count < k) {  
  9.        next  = current.next;  
  10.        current.next = prev;  
  11.        prev = current;  
  12.        current = next;  
  13.        count++;  
  14.     }  
  15.    
  16.     /* next is now a pointer to (k+1)th node 
  17.        Recursively call for the list starting from current. 
  18.        And make rest of the list as next of first node */  
  19.     if(next !=  null) {  
  20.         head.next = reverse(next, k);   
  21.     }  
  22.    
  23.     /* prev is new head of the input list */  
  24.     return prev;  
  25. }  

 

这个问题也可以使用非递归的方法,基本上问题的处理方式和递归是一样的,但是,非递归的方式要稍微复杂一点,因为每次对子链表反转以后,我们需要更新前一个子链表最后一个node 的next 值。代码如下:

 

[java] view plain copy

 

  1. class Node {  
  2.      int val;  
  3.      Node next;  
  4.      Node(int x) {  
  5.          val = x;  
  6.          next = null;  
  7.      }  
  8. }  
  9.   
  10. public class Solution {  
  11.       
  12.     public static void main(String[] args) {  
  13.         Solution s = new Solution();  
  14.           
  15.         Node n1 = new Node(1);  
  16.         Node n2 = new Node(2);  
  17.         Node n3 = new Node(3);  
  18.         Node n4 = new Node(4);  
  19.         Node n5 = new Node(5);  
  20.           
  21.         n1.next = n2;  
  22.         n2.next = n3;  
  23.         n3.next = n4;  
  24.         n4.next = n5;  
  25.           
  26.         Node head = s.ReverseInGroups(n1, 2);  
  27.         while (head != null) {  
  28.             System.out.println(head.val);  
  29.             head = head.next;  
  30.         }  
  31.     }  
  32.     public Node ReverseInGroups(Node current, int k) {  
  33.         if (current == null || current.next == null ) return current;  
  34.         //store the new head of the list  
  35.         Node newHead = null;  
  36.           
  37.         //store the last node in the sub-list,   
  38.         //we will update its next value when finishing  
  39.         //reversing the next sub-list  
  40.         Node previousGroupTail = null;  
  41.         int count = 1; //used to track the first sub-list  
  42.         while (current != null) {     
  43.             // the actual tail in the sub-list  
  44.             Node groupTail = current;  
  45.             //reverse  
  46.             Node prev = null;  
  47.             Node next = null;  
  48.             for (int i = 1; i <= k && current != null; i++) {  
  49.                 next = current.next;  
  50.                 current.next = prev;  
  51.                 prev = current;  
  52.                 current = next;  
  53.             }  
  54.             // get the new head of the whole list  
  55.             if (count == 1) {  
  56.                 newHead = prev;  
  57.                 count++;  
  58.             }  
  59.             // update the next value of the last node in  
  60.             // each sub-list  
  61.             if (previousGroupTail != null) {  
  62.                 previousGroupTail.next = prev;  
  63.             }  
  64.             previousGroupTail = groupTail;  
  65.         }  
  66.         return newHead;  
  67.     }  

转载于:https://my.oschina.net/u/2822116/blog/805154

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值