LeetCode61.旋转链表 Java实现


61. 旋转链表

题目来源

61. 旋转链表

题目分析

题目描述

给你一个链表的头节点 head,旋转链表,将链表每个节点向右移动 k 个位置。

题目难度

  • 难度:简单

题目标签

  • 标签:链表

题目限制

  • 链表中节点的数目在范围 [0, 500]
  • -100 <= Node.val <= 100
  • 0 <= k <= 2 * 10^9

解题思路

思路:模拟旋转过程

  1. 问题定义
    • 需要将链表的节点向右移动 k 个位置。
  2. 核心算法
    • 首先计算链表的长度 len
    • 计算实际需要移动的步数 k % len,以避免不必要的完整循环。
    • 使用双指针找到新的头节点和尾节点。
  3. 关键步骤
    • 计算链表长度 len
    • 计算实际需要移动的步数 k % len
    • 使用两个指针 precur,先将 cur 移动 k % len 步。
    • 然后同时移动 precur,直到 cur 到达链表的最后一个节点。
    • cur.next 指向原来的头节点 head,形成一个环。
    • 更新新的头节点为 pre.next
    • pre.next 置为 null,断开环。
  4. 返回结果
    • 返回新的头节点。

核心算法步骤

  1. 计算链表长度
    • 初始化 len 为 1。
    • 使用 tail 指针遍历链表,计算链表的总长度 len
  2. 计算实际移动步数
    • 计算 k % len,得到实际需要移动的步数。
  3. 使用双指针找到新的头节点和尾节点
    • 初始化 precur 指针都指向头节点 head
    • 先将 cur 指针移动 k % len 步。
    • 然后同时移动 precur 指针,直到 cur 到达链表的最后一个节点。
  4. 更新链表
    • cur.next 指向原来的头节点 head,形成一个环。
    • 更新新的头节点为 pre.next
    • pre.next 置为 null,断开环。
  5. 返回结果
    • 返回新的头节点。

代码实现

以下是 Java 代码实现:

/**
 * 61. 旋转链表
 * @param head 链表头结点
 * @param k    移动位置
 * @return ListNode 旋转后的链表头结点
 */
public ListNode rotateRight(ListNode head, int k) {
    if (head == null || head.next == null || k == 0) return head;
    int len = 1;
    ListNode pre = head;
    ListNode cur = head;
    ListNode tail = head;
    while (tail.next != null) {
        tail = tail.next;
        len++;
    }
    k = k % len;
    for (int i = 0; i < k; i++) {
        cur = cur.next;
    }
    while (cur.next != null) {
        pre = pre.next;
        cur = cur.next;
    }
    cur.next = head;
    head = pre.next;
    pre.next = null;
    return head;
}

代码解读

  • 计算链表长度:通过遍历链表,计算链表的总长度 len
  • 计算实际移动步数:计算 k % len,得到实际需要移动的步数。
  • 使用双指针找到新的头节点和尾节点
    • 先将 cur 指针移动 k % len 步。
    • 然后同时移动 precur 指针,直到 cur 到达链表的最后一个节点。
  • 更新链表
    • cur.next 指向原来的头节点 head,形成一个环。
    • 更新新的头节点为 pre.next
    • pre.next 置为 null,断开环。
  • 返回结果:返回新的头节点。

性能分析

  • 时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表两次,一次计算长度,一次找到新的头节点。
  • 空间复杂度:O(1),除了用于存储结果的链表外,使用的额外空间是常数级别的。

测试用例

你可以使用以下测试用例来验证代码的正确性:

// 测试用例1
ListNode head1 = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
int k1 = 2;
System.out.println(rotateRight(head1, k1)); // 输出: [4, 5, 1, 2, 3]

// 测试用例2
ListNode head2 = new ListNode(0, new ListNode(1, new ListNode(2)));
int k2 = 4;
System.out.println(rotateRight(head2, k2)); // 输出: [2, 0, 1]

// 测试用例3
ListNode head3 = new ListNode(1, new ListNode(2));
int k3 = 1;
System.out.println(rotateRight(head3, k3)); // 输出: [2, 1]

// 测试用例4
ListNode head4 = new ListNode(1);
int k4 = 0;
System.out.println(rotateRight(head4, k4)); // 输出: [1]

// 测试用例5
ListNode head5 = null;
int k5 = 3;
System.out.println(rotateRight(head5, k5)); // 输出: null

扩展讨论

其他实现

  • 递归方法:虽然递归可以实现链表的旋转,但可能会增加代码的复杂度,并且对于这个问题,迭代方法已经足够清晰和高效。
  • 方向数组:对于链表旋转问题,方向数组并不是必需的,因为旋转操作主要涉及指针的重新连接。

性能优化

  • 空间优化:在上述实现中,我们已经做到了空间复杂度为 O(1),除了输出链表外没有使用额外的空间。
  • 边界检查:在计算实际移动步数时,通过 k % len 避免了不必要的完整循环,这是性能优化的一部分。

总结

这道题目考察了对链表操作的理解和实现能力。通过模拟旋转的过程,我们可以有效地解决这个问题。在实际应用中,类似的链表操作可能会用于数据结构的转换、图像处理等领域。掌握这种操作对于解决更复杂的问题具有重要的基础意义。


希望这份文档能够帮助你更好地理解和实现旋转链表的问题!如果有任何进一步的问题或需要更多的优化,请随时告知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值