大厂面试手撕面试题:反转链表中前 K 个节点(亲测可用的java实现)

我们需要反转链表中前 k 个节点,且可能会有多个这样的反转段(每 k 个节点为一组)。如果链表的剩余部分不满 k 个节点,我们就不对其进行反转,直接保留原样。

解题思路:

  1. 链表结构:
    • 我们有一个单链表,定义为 ListNode 类型。每个节点包含一个值 val 和指向下一个节点的指针 next
    • 目标是反转链表中前 k 个节点,并对每一组长度为 k 的节点进行反转,直到链表遍历完。
  2. 关键步骤:
    • 判断当前段是否满 k 个节点: 在反转之前,我们必须确认当前的节点数是否足够 k 个。如果不足,直接跳过该段,保持原链表顺序。
    • 反转当前 k 个节点: 如果当前段正好有 k 个节点,我们就进行反转。
    • 连接反转后的链表: 反转后的链表需要和之前的链表正确连接,以保证链表结构的完整性。
  3. 具体步骤:
    • 维护一个 dummy 虚拟节点,它的 next 指针指向链表头。这样可以避免链表头部的特殊情况处理,使得头节点的反转与其他节点反转保持一致。
    • 使用 prevGroupEnd 指针来标记上一个反转段的最后一个节点,它在反转完成后需要连接到当前反转段的最后一个节点。
    • 对每一组长度为 k 的节点进行反转。具体做法是使用 prev 和 curr 两个指针,通过迭代的方式完成每组的反转。
    • 完成一组反转后,更新 prevGroupEnd 和 head,继续处理下一组。
  4. 终止条件:
    • 当链表遍历完,或者剩下的节点不足 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);
    }
}

详细解析:

  1. 虚拟节点 dummy
    • dummy 节点的作用是简化链表头部的操作,避免对链表头的特殊处理。通过将 dummy.next 设置为链表的头节点,我们可以统一处理链表的反转和连接。
  2. 变量 prevGroupEnd
    • prevGroupEnd 用于连接每次反转后组的最后一个节点。初始化时,它指向虚拟节点 dummy
    • 在每次反转完成后,prevGroupEnd 被更新为当前反转组的头节点 head
  3. 变量 groupEndcount
    • groupEnd 用于记录当前反转组的最后一个节点。如果当前组不足 k 个节点,则不进行反转。
    • count 用来计算当前组的节点数。如果 count 达到 k,则进行反转操作。
  4. 反转操作:
    • 反转时,通过三个指针 prev(指向反转后的部分),curr(当前处理的节点),和 temp(暂存当前节点的下一个节点)来完成每一组的节点反转。
    • 完成反转后,将 prevGroupEnd 的 next 指向当前反转组的尾节点 groupEnd,并将 head.next 指向剩余部分的起始节点。
  5. 循环和终止条件:
    • 每次反转 k 个节点后,将 head 更新为剩余部分的起始节点,继续进行下一组的处理。
    • 当剩余节点不足 k 个时,循环终止。

时间复杂度:

  • O(n): 每个节点会被访问并反转一次,总时间复杂度为 O(n),其中 n 是链表的节点数。

空间复杂度:

  • O(1): 只使用了常数的额外空间,除了输入的链表本身。

示例:

对于链表 1 -> 2 -> 3 -> 4 -> 5k = 3,执行反转操作后,链表将变为 3 -> 2 -> 1 -> 4 -> 5

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。更多免费资源在http://www.gitweixin.com/?p=2627

发表评论

邮箱地址不会被公开。 必填项已用*标注