leetcode
leetcode 101 ~ 150
重排链表

重排链表

难度:

标签:

题目描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

 

示例 1:

输入:head = [1,2,3,4]
输出:[1,4,2,3]

示例 2:

输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

 

提示:

  • 链表的长度范围为 [1, 5 * 104]
  • 1 <= node.val <= 1000

代码结果

运行时间: 108 ms, 内存: 23.1 MB


/*
 题目思路:
 使用Java Stream API进行链表处理的难度较大,因此我们以另一种方式实现。
 1. 使用传统方法找到中间节点。
 2. 反转后半部分链表。
 3. 交替合并两个部分。
 
 代码实现:
*/
 
// 由于Stream API不适合处理链表节点交换问题,因此没有使用Stream API的解决方案。

解释

方法:

该题解的思路可以分为以下几个步骤: 1. 使用快慢指针找到链表的中点,将链表分为前后两个部分 l1 和 l2。 2. 反转后半部分链表 l2。 3. 将 l1 和反转后的 l2 依次连接,得到重排后的链表。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在使用快慢指针找链表中点时,如何确保当链表长度为奇数时,中点正确地指向中间节点而不是中间之前或之后的节点?
当使用快慢指针方法确定链表中点时,快指针每次移动两步,慢指针每次移动一步。在链表长度为奇数时,快指针会在达到末尾的null节点停止(即快指针的next或next.next为null时)。此时慢指针正好位于中间节点。例如,在一个具有5个节点的链表中,当快指针到达末尾的null时,慢指针将位于第3个节点,正是中间的节点。这种方法确保了慢指针停在正中间的节点上,符合题目的要求。
🦆
反转链表部分中,为何选择s.next作为新的链表l2的起点,而不是直接从s开始反转?
在题解中,s指针在快慢指针的过程中最终指向前半部分链表的最后一个节点。因此,s.next自然指向后半部分链表的第一个节点,这就是新的链表l2的起点。从s开始反转会将前半部分的尾部也包括进来,这与题目要求重排链表而不是整体反转链表是不符的。因此,选择s.next作为起点是为了正确分离两个部分,仅对后半部分进行反转处理。
🦆
在合并链表的过程中,如果l1的长度大于l2的长度,最后一个节点的处理是否有特殊的考虑或步骤?
在合并链表的过程中,如果l1的长度大于l2的长度,会存在l1中还有剩余节点而l2已经全部插入的情况。由于题解中的合并过程是交替进行的,当l2节点用尽时,l1中的剩余节点已经正确地位于链表的末尾,不需要额外的操作。合并操作自然终止在l2节点用尽时,剩余的l1节点保持其原有的顺序即可。
🦆
如果链表非常长,递归反转l2可能会导致栈溢出。是否有非递归的方法来反转链表?
当处理非常长的链表时,递归反转确实可能因为深度过大导致栈溢出。可以使用非递归的方法来反转链表,这通常涉及使用迭代方式。在迭代方法中,我们使用两个指针,pre和cur,初始化pre为null,cur为链表的头节点。在迭代过程中,将cur的next指向pre,然后更新pre和cur的值,直到cur为null。这种方法不涉及递归调用,因此不会导致栈溢出,适合处理大型数据。

相关问题