题目
题目来源:力扣(LeetCode)
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
示例 1:
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
示例 2:
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
思路分析
双指针 分离链表
如果链表为空,则直接返回链表。
对于原始链表,每个节点都是奇数节点或偶数节点。头节点是奇数节点,头节点的后一个节点是偶数节点,相邻节点的奇偶性不同。因此可以将奇数节点和偶数节点分离成奇数链表和偶数链表,然后将偶数链表连接在奇数链表之后,合并后的链表即为结果链表。
原始链表的头节点 head 也是奇数链表的头节点以及结果链表的头节点,head 的后一个节点是偶数链表的头节点。令 evenHead = head.next,则 evenHead 是偶数链表的头节点。
维护两个指针 odd 和 even 分别指向奇数节点和偶数节点,初始时 odd = head,even = evenHead。通过迭代的方式将奇数节点和偶数节点分离成两个链表,每一步首先更新奇数节点,然后更新偶数节点。
更新奇数节点时,奇数节点的后一个节点需要指向偶数节点的后一个节点,因此令 odd.next = even.next,然后令 odd = odd.next,此时 odd 变成 even 的后一个节点。
更新偶数节点时,偶数节点的后一个节点需要指向奇数节点的后一个节点,因此令 even.next = odd.next,然后令 even = even.next,此时 even 变成 odd 的后一个节点。
在上述操作之后,即完成了对一个奇数节点和一个偶数节点的分离。重复上述操作,直到全部节点分离完毕。全部节点分离完毕的条件是 even 为空节点或者 even.next 为空节点,此时 odd 指向最后一个奇数节点(即奇数链表的最后一个节点)。
最后令 odd.next = evenHead,将偶数链表连接在奇数链表之后,即完成了奇数链表和偶数链表的合并,结果链表的头节点仍然是 head。
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var oddEvenList = function (head) {
// 如果链表为空,则直接返回链表
if (head === null) {
return head;
}
// 根据相邻节点的奇偶性,将原始链表分离成奇数链表和偶数链表
// 对于原始链表,头节点是奇数节点,头节点的后一个节点是偶数节点
// 维护指针 odd 指向奇数节点
// 维护指针 even 指向偶数节点
// 初始时,odd 指针指向原始链表的头节点
let odd = head; // 令原始链表的头节点为奇数链表的头节点
let evenHead = head.next; // 令原始链表头节点的后一个节点(即偶数节点) 为偶数链表的头节点
// 初始时,even 指针指向原始链表的头节点的下一个节点
let even = evenHead;
// 遍历链表,将奇数节点和偶数节点分离成两个链表
while (even !== null && even.next !== null) {
// 偶数节点的下一个节点,即 even.next 是下一个奇数节点,因此将奇数节点的下一个节点指向 even.next
odd.next = even.next;
// 移动 odd 指针,指向偶数节点的下一个节点,即 even.next(下一个奇数节点)
odd = odd.next;
// 奇数节点的下一个节点,即 odd.next 是下一个偶数节点,因此将偶数节点的下一个节点指向 odd.next
even.next = odd.next;
// 移动 even 指针,指奇数节点的下一个节点,即 odd.next(下一个偶数节点)
even = even.next;
}
// 将偶数链表连接在奇数链表后面
odd.next = evenHead;
return head;
};
快慢指针
时间复杂度为O(1/2n)也就是O(n) 解法:
- 使用快慢指针;每次更新快慢指针跳过1个节点
- 用奇数指针的最后一项连接偶数指针
最终就得到原来的结果
var oddEvenList = function (head) {
// 如果链表为空,则直接返回链表
if (!head || !head.next) return head;
// 对于原始链表,每个节点都是奇数节点或偶数节点。头节点是奇数节点,头节点的后一个节点是偶数节点
let odd = head; // 奇数指针,初始时指向原始链表的头节点
let even = head.next; // 偶数指针,初始时指向原始链表头节点的下一个节点
let targetOdd = new ListNode(); // 当前为奇数链表
let oddNode = targetOdd; // 当前为奇数链表当前的节点
let targetEven = new ListNode(); // 当前为偶数链表
let evenNode = targetEven; // 当前为偶数链表的节点
// 这里的时间复杂度O(1/2n)
while (odd || even) {
// 分别增加奇数链表以及偶数链表的节点
if (odd) {
oddNode.next = new ListNode(odd.val);
oddNode = oddNode.next;
}
if (even) {
evenNode.next = new ListNode(even.val);
evenNode = evenNode.next;
}
// 查找下一个奇数或者偶数节点,当下一个节点的时为null时
// 把当前的节点置为null防止死循环
if (odd) {
if (odd.next) {
//根据链表节点编号的奇偶性, 奇数节点的下一个节点的下一个节点是奇数
odd = odd.next.next;
} else {
odd = null;
}
}
if (even) {
if (even.next) {
//根据链表节点编号的奇偶性, 偶数节点的下一个节点的下一个节点是偶数
even = even.next.next;
} else {
even = null;
}
}
}
// 剩下的为最后一个节点连接偶数节点最终返回的就是所需要的
oddNode.next = targetEven.next;
return targetOdd.next;
};