携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情
题目描述
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
示例 1:
输入: head = [1,2,3,4,5] 输出: [1,3,5,2,4] 示例 2:
输入: head = [2,1,3,5,6,4,7] 输出: [2,3,6,7,1,5,4]
提示:
- n == 链表中的节点数
- 0 <= n <= 104
- -106 <= Node.val <= 106
题目元素
给定一个链表,将链表中的节点按照元素所在索引的奇偶性分开,奇数在前,偶数在后,除此之外链表元素
链表元素交换,思路仍旧是三个元素
哑元节点
设置一个值为空的链表,将真实链表放入它的next属性,这样在操作所有链表时不用考虑首节点的特殊性,这里以为是将奇偶数分开,所以可以把这个问题看成将链表中的元素按照奇偶性分成2个链表,所以这里需要2个哑元节点;
虚拟节点
这里需要将奇偶数分开,所以需要2个虚拟节点,它们分别指向奇数链表和偶数链表的当前操作节点,每次移动后该值都会向后移动,结束之后只需要将奇数的虚拟节点所在的next节点设置为偶数的雅苑节点的next,即可得到最终答案。
链表移动
链表的移动其实就是将当前操作节点向后移动,并将节点的next赋值到虚拟节点上,截止条件为当前操作节点为空。
难点在于链表对象的重新指向,可以多总结做题规律,然后会发现它们的套路都是差不多的。
实现步骤
创建一个当前遍历的操作节点tail,即链表移动的当前节点;
为奇偶链表分别设置哑元节点和虚拟节点,虚拟节点分别指向奇数链表和偶数链表的最后一个元素;
设置一个索引下标index,初始值为1,因为根据题意所以是从1开始计算的;
遍历tail,判断索引奇偶性,根据奇偶性将它们放入对应的链表中,此时注意指针后移,即奇偶链表的虚拟节点始终指向它所代表的链表的最后一个元素;
链表移动,将tail赋值为tail.next,且索引自增1;
循环结束后,即链表内所有元素都重新放入了对应的奇偶链表中,此时需要注意,因为偶数链表是放在后面的,所以它的最后一个元素next一定要重新设置为null;
将偶数链表放入奇数最后一个节点的next中,然后再将偶数链表的哑元节点的next(真实链表)放入奇数链表的最后一个节点的next中。
最终的奇数哑元节点的next就是生成的新链表。
代码实现
public static ListNode oddEvenList(ListNode head) {
// 设置当前操作节点 初始化为一个哑元节点,即将真实需要遍历的节点放入这个节点的next中,每次遍历next即可
ListNode tail = new ListNode();
tail.next = head;
// 设置奇偶链表的哑元节点
ListNode oddDummyNode = new ListNode();
ListNode evenDummyNode = new ListNode();
// 奇数 指向最后一个操作的节点
ListNode oddTail = oddDummyNode;
// 偶数 指向最后一个操作的节点
ListNode evenTail = evenDummyNode;
int index = 1;
while (tail.next != null) {
if (index % 2 == 0) {
// 偶数
evenTail.next = tail.next;
// 始终指向最后一个操作节点
evenTail = tail.next;
} else {
// 奇数
oddTail.next = tail.next;
oddTail = tail.next;
}
tail = tail.next;
index ++;
}
evenTail.next = null;
oddTail.next = evenDummyNode.next;
// 交换位置
return oddDummyNode.next;
}