题目
题目来源:力扣(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.nextodd.next = even.next;// 移动 odd 指针,指向偶数节点的下一个节点,即 even.next(下一个奇数节点)odd = odd.next;// 奇数节点的下一个节点,即 odd.next 是下一个偶数节点,因此将偶数节点的下一个节点指向 odd.nexteven.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;};
