大厂面试手撕面试题:反转链表中前 K 个节点(亲测可用的java实现)
我们需要反转链表中前 k 个节点,且可能会有多个这样的反转段(每 k 个节点为一组)。如果链表的剩余部分不满 k 个节点,我们就不对其进行反转,直接保留原样。
解题思路:
- 链表结构:
- 我们有一个单链表,定义为 
ListNode类型。每个节点包含一个值val和指向下一个节点的指针next。 - 目标是反转链表中前 
k个节点,并对每一组长度为k的节点进行反转,直到链表遍历完。 
 - 我们有一个单链表,定义为 
 - 关键步骤:
- 判断当前段是否满 
k个节点: 在反转之前,我们必须确认当前的节点数是否足够k个。如果不足,直接跳过该段,保持原链表顺序。 - 反转当前 
k个节点: 如果当前段正好有k个节点,我们就进行反转。 - 连接反转后的链表: 反转后的链表需要和之前的链表正确连接,以保证链表结构的完整性。
 
 - 判断当前段是否满 
 - 具体步骤:
- 维护一个 
dummy虚拟节点,它的next指针指向链表头。这样可以避免链表头部的特殊情况处理,使得头节点的反转与其他节点反转保持一致。 - 使用 
prevGroupEnd指针来标记上一个反转段的最后一个节点,它在反转完成后需要连接到当前反转段的最后一个节点。 - 对每一组长度为 
k的节点进行反转。具体做法是使用prev和curr两个指针,通过迭代的方式完成每组的反转。 - 完成一组反转后,更新 
prevGroupEnd和head,继续处理下一组。 
 - 维护一个 
 - 终止条件:
- 当链表遍历完,或者剩下的节点不足 
k个时,停止反转操作。 
 - 当链表遍历完,或者剩下的节点不足 
 
代码详解:
public class Solution {
    
    public ListNode reverseKGroup(ListNode head, int k) {
        // 如果链表为空或 k 为 1,直接返回头节点
        if (head == null || k == 1) return head;
        
        // 创建一个虚拟节点,用于简化操作
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        
        // prevGroupEnd 用于指向前一组反转后链表的末尾节点
        ListNode prevGroupEnd = dummy;
        
        // 当前头节点 head
        while (head != null) {
            // 找到当前组的最后一个节点
            ListNode groupEnd = head;
            int count = 1;
            // 计算当前组的节点数量
            while (groupEnd != null && count < k) {
                groupEnd = groupEnd.next;
                count++;
            }
            
            // 如果当前组的节点数不足 K 个,不需要反转,直接返回
            if (groupEnd == null) break;
            
            // 反转当前组的节点
            ListNode nextGroupStart = groupEnd.next;  // 保存下一个组的起始节点
            ListNode prev = nextGroupStart;  // prev 初始指向下一组的起始节点
            ListNode curr = head;  // curr 初始指向当前组的头节点
            
            // 反转当前组的节点
            while (curr != nextGroupStart) {
                ListNode temp = curr.next;  // 暂存当前节点的下一个节点
                curr.next = prev;  // 将当前节点的 next 指向 prev(反转)
                prev = curr;  // prev 更新为当前节点
                curr = temp;  // curr 更新为下一个节点
            }
            
            // 将上一组的尾节点连接到当前反转后的组的头节点
            prevGroupEnd.next = groupEnd;
            // 当前组的头节点(反转后)需要指向下一组的起始节点
            head.next = nextGroupStart;
            
            // 更新 prevGroupEnd 和 head,准备处理下一组
            prevGroupEnd = head;
            head = nextGroupStart;
        }
        
        return dummy.next; // 返回新的链表头节点
    }
    // 辅助方法:打印链表
    public void printList(ListNode head) {
        while (head != null) {
            System.out.print(head.val + " ");
            head = head.next;
        }
        System.out.println();
    }
    public static void main(String[] args) {
        Solution solution = new Solution();
        
        // 创建一个链表 1 -> 2 -> 3 -> 4 -> 5
        ListNode head = new ListNode(1);
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        head.next.next.next = new ListNode(4);
        head.next.next.next.next = new ListNode(5);
        
        int k = 3;
        
        // 输出原链表
        System.out.print("Original List: ");
        solution.printList(head);
        
        // 调用反转链表前 K 个节点的方法
        ListNode newHead = solution.reverseKGroup(head, k);
        
        // 输出反转后的链表
        System.out.print("List after reversing first " + k + " nodes: ");
        solution.printList(newHead);
    }
}
详细解析:
- 虚拟节点 
dummy:dummy节点的作用是简化链表头部的操作,避免对链表头的特殊处理。通过将dummy.next设置为链表的头节点,我们可以统一处理链表的反转和连接。
 - 变量 
prevGroupEnd:prevGroupEnd用于连接每次反转后组的最后一个节点。初始化时,它指向虚拟节点dummy。- 在每次反转完成后,
prevGroupEnd被更新为当前反转组的头节点head。 
 - 变量 
groupEnd和count:groupEnd用于记录当前反转组的最后一个节点。如果当前组不足k个节点,则不进行反转。count用来计算当前组的节点数。如果count达到k,则进行反转操作。
 - 反转操作:
- 反转时,通过三个指针 
prev(指向反转后的部分),curr(当前处理的节点),和temp(暂存当前节点的下一个节点)来完成每一组的节点反转。 - 完成反转后,将 
prevGroupEnd的next指向当前反转组的尾节点groupEnd,并将head.next指向剩余部分的起始节点。 
 - 反转时,通过三个指针 
 - 循环和终止条件:
- 每次反转 
k个节点后,将head更新为剩余部分的起始节点,继续进行下一组的处理。 - 当剩余节点不足 
k个时,循环终止。 
 - 每次反转 
 
时间复杂度:
- O(n): 每个节点会被访问并反转一次,总时间复杂度为 O(n),其中 n 是链表的节点数。
 
空间复杂度:
- O(1): 只使用了常数的额外空间,除了输入的链表本身。
 
示例:
对于链表 1 -> 2 -> 3 -> 4 -> 5 和 k = 3,执行反转操作后,链表将变为 3 -> 2 -> 1 -> 4 -> 5。
