奇偶链表
问题描述
给定单链表的头节点 head,把链表中的所有奇数位置节点(第1、3、5…个节点)放在一起,然后是所有偶数位置节点(第2、4、6…个节点),并返回重新排列后的链表。
注意:这里的奇偶指的是节点的位置(从1开始计数),而不是节点的值。
要求:
- 空间复杂度应为 O(1)
- 时间复杂度应为 O(n)
- 必须保持奇数节点和偶数节点的相对顺序
示例:
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
算法思路
核心思想:分离-连接
- 分离:遍历链表,将奇数位置节点连接成一个链表,偶数位置节点连接成另一个链表
- 连接:将奇数链表的尾部连接到偶数链表的头部
- 关键:使用两个指针分别维护奇数链表和偶数链表,同时记录偶数链表的头节点
代码实现
方法一:双指针分离
/**
* Definition for singly-linked list.
*/
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
/**
* 奇偶链表重排:将奇数位置节点放在前面,偶数位置节点放在后面
*
* 算法思路:
* 1. 使用两个指针 odd 和 even 分别指向奇数位置和偶数位置的当前节点
* 2. 保存 evenHead 作为偶数链表的起始位置
* 3. 遍历过程中,odd 指向 odd.next.next,even 指向 even.next.next
* 4. 最后将 odd 的尾部连接到 evenHead
*
* @param head 链表头节点
* @return 重排后的链表头节点
*
* 时间复杂度:O(n),只需遍历一次链表
* 空间复杂度:O(1),只使用常数额外空间
*/
public ListNode oddEvenList(ListNode head) {
// 边界情况:空链表或只有一个节点
if (head == null || head.next == null) {
return head;
}
// 初始化指针
ListNode odd = head; // 奇数位置指针,从第1个节点开始
ListNode even = head.next; // 偶数位置指针,从第2个节点开始
ListNode evenHead = even; // 保存偶数链表的头节点,用于最后连接
// 遍历链表,分离奇偶节点
// 继续条件:even != null 且 even.next != null
// 因为 even 在 odd 后面,所以只需要检查 even 的情况
while (even != null && even.next != null) {
// 连接下一个奇数节点
odd.next = even.next;
odd = odd.next; // 移动 odd 指针
// 连接下一个偶数节点
even.next = odd.next;
even = even.next; // 移动 even 指针
}
// 连接奇数链表和偶数链表
odd.next = evenHead;
return head; // 返回原头节点(奇数链表头)
}
}
算法分析
时间复杂度
- O(n):只需要遍历链表一次,每个节点访问一次
空间复杂度
- O(1):只使用了固定数量的额外指针变量
算法过程
1->2->3->4->5->NULL :
初始状态:
odd = 1,even = 2,evenHead = 2- 链表:
1->2->3->4->5->NULL
第一次循环:
odd.next = 3→ 奇数链表:1->3odd = 3even.next = 4→ 偶数链表:2->4even = 4- 当前状态:奇数链表
1->3,偶数链表2->4->5->NULL
第二次循环:
odd.next = 5→ 奇数链表:1->3->5odd = 5even.next = null→ 偶数链表:2->4->NULLeven = null
循环结束:
- 连接:
odd.next = evenHead→5->2 - 最终结果:
1->3->5->2->4->NULL
测试用例
public class TestOddEvenList {
/**
* 创建链表
*/
private static ListNode createList(int[] values) {
if (values.length == 0) return null;
ListNode head = new ListNode(values[0]);
ListNode current = head;
for (int i = 1; i < values.length; i++) {
current.next = new ListNode(values[i]);
current = current.next;
}
return head;
}
/**
* 打印链表
*/
private static String printList(ListNode head) {
if (head == null) return "NULL";
StringBuilder sb = new StringBuilder();
ListNode current = head;
while (current != null) {
sb.append(current.val);
if (current.next != null) sb.append("->");
current = current.next;
}
sb.append("->NULL");
return sb.toString();
}
public static void main(String[] args) {
Solution solution = new Solution();
// 测试用例1:标准示例
ListNode head1 = createList(new int[]{1, 2, 3, 4, 5});
ListNode result1 = solution.oddEvenList(head1);
System.out.println("Test 1: " + printList(result1)); // 1->3->5->2->4->NULL
// 测试用例2:偶数长度链表
ListNode head2 = createList(new int[]{2, 1, 3, 5, 6, 4});
ListNode result2 = solution.oddEvenList(head2);
System.out.println("Test 2: " + printList(result2)); // 2->3->6->1->5->4->NULL
// 测试用例3:单个节点
ListNode head3 = createList(new int[]{1});
ListNode result3 = solution.oddEvenList(head3);
System.out.println("Test 3: " + printList(result3)); // 1->NULL
// 测试用例4:两个节点
ListNode head4 = createList(new int[]{1, 2});
ListNode result4 = solution.oddEvenList(head4);
System.out.println("Test 4: " + printList(result4)); // 1->2->NULL
// 测试用例5:空链表
ListNode head5 = null;
ListNode result5 = solution.oddEvenList(head5);
System.out.println("Test 5: " + printList(result5)); // NULL
// 测试用例6:三个节点
ListNode head6 = createList(new int[]{1, 2, 3});
ListNode result6 = solution.oddEvenList(head6);
System.out.println("Test 6: " + printList(result6)); // 1->3->2->NULL
// 测试用例7:四个节点
ListNode head7 = createList(new int[]{1, 2, 3, 4});
ListNode result7 = solution.oddEvenList(head7);
System.out.println("Test 7: " + printList(result7)); // 1->3->2->4->NULL
// 测试用例8:包含重复值
ListNode head8 = createList(new int[]{1, 1, 1, 1, 1});
ListNode result8 = solution.oddEvenList(head8);
System.out.println("Test 8: " + printList(result8)); // 1->1->1->1->1->NULL
}
}
关键点
-
位置 vs 值:
- 要求按位置的奇偶性分组,不是按节点值的奇偶性
- 第1个节点(位置1)是奇数位置,第2个节点(位置2)是偶数位置
-
指针移动顺序:
- 必须先更新
odd.next,再更新even.next - 因为
even.next依赖于更新后的odd.next
- 必须先更新
-
循环终止条件:
while (even != null && even.next != null)- 因为
even在odd后面,所以检查even的情况就足够了 - 如果
even为 null,说明已经到达链表末尾 - 如果
even.next为 null,说明下一个奇数位置不存在
-
边界处理:
- 空链表:直接返回 null
- 单节点链表:直接返回原链表
常见问题
-
为什么循环条件只检查 even 而不检查 odd?
- 因为 even 总是在 odd 的后面,如果 even 还存在,那么 odd 肯定也存在
- 当 even 为 null 或 even.next 为 null 时,说明没有更多的偶数或奇数节点需要处理
-
为什么要保存 evenHead?
- 因为在遍历过程中,even 指针会不断移动,最终指向偶数链表的尾部
- 需要保存最初的偶数链表头部,用于最后将奇数链表连接到偶数链表

被折叠的 条评论
为什么被折叠?



