题目

题目来源:力扣(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。

  1. /**
  2. * Definition for singly-linked list.
  3. * function ListNode(val, next) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.next = (next===undefined ? null : next)
  6. * }
  7. */
  8. /**
  9. * @param {ListNode} head
  10. * @return {ListNode}
  11. */
  12. var oddEvenList = function (head) {
  13. // 如果链表为空,则直接返回链表
  14. if (head === null) {
  15. return head;
  16. }
  17. // 根据相邻节点的奇偶性,将原始链表分离成奇数链表和偶数链表
  18. // 对于原始链表,头节点是奇数节点,头节点的后一个节点是偶数节点
  19. // 维护指针 odd 指向奇数节点
  20. // 维护指针 even 指向偶数节点
  21. // 初始时,odd 指针指向原始链表的头节点
  22. let odd = head; // 令原始链表的头节点为奇数链表的头节点
  23. let evenHead = head.next; // 令原始链表头节点的后一个节点(即偶数节点) 为偶数链表的头节点
  24. // 初始时,even 指针指向原始链表的头节点的下一个节点
  25. let even = evenHead;
  26. // 遍历链表,将奇数节点和偶数节点分离成两个链表
  27. while (even !== null && even.next !== null) {
  28. // 偶数节点的下一个节点,即 even.next 是下一个奇数节点,因此将奇数节点的下一个节点指向 even.next
  29. odd.next = even.next;
  30. // 移动 odd 指针,指向偶数节点的下一个节点,即 even.next(下一个奇数节点)
  31. odd = odd.next;
  32. // 奇数节点的下一个节点,即 odd.next 是下一个偶数节点,因此将偶数节点的下一个节点指向 odd.next
  33. even.next = odd.next;
  34. // 移动 even 指针,指奇数节点的下一个节点,即 odd.next(下一个偶数节点)
  35. even = even.next;
  36. }
  37. // 将偶数链表连接在奇数链表后面
  38. odd.next = evenHead;
  39. return head;
  40. };

快慢指针
时间复杂度为O(1/2n)也就是O(n) 解法:

  1. 使用快慢指针;每次更新快慢指针跳过1个节点
  2. 用奇数指针的最后一项连接偶数指针
  3. 最终就得到原来的结果

    1. var oddEvenList = function (head) {
    2. // 如果链表为空,则直接返回链表
    3. if (!head || !head.next) return head;
    4. // 对于原始链表,每个节点都是奇数节点或偶数节点。头节点是奇数节点,头节点的后一个节点是偶数节点
    5. let odd = head; // 奇数指针,初始时指向原始链表的头节点
    6. let even = head.next; // 偶数指针,初始时指向原始链表头节点的下一个节点
    7. let targetOdd = new ListNode(); // 当前为奇数链表
    8. let oddNode = targetOdd; // 当前为奇数链表当前的节点
    9. let targetEven = new ListNode(); // 当前为偶数链表
    10. let evenNode = targetEven; // 当前为偶数链表的节点
    11. // 这里的时间复杂度O(1/2n)
    12. while (odd || even) {
    13. // 分别增加奇数链表以及偶数链表的节点
    14. if (odd) {
    15. oddNode.next = new ListNode(odd.val);
    16. oddNode = oddNode.next;
    17. }
    18. if (even) {
    19. evenNode.next = new ListNode(even.val);
    20. evenNode = evenNode.next;
    21. }
    22. // 查找下一个奇数或者偶数节点,当下一个节点的时为null时
    23. // 把当前的节点置为null防止死循环
    24. if (odd) {
    25. if (odd.next) {
    26. //根据链表节点编号的奇偶性, 奇数节点的下一个节点的下一个节点是奇数
    27. odd = odd.next.next;
    28. } else {
    29. odd = null;
    30. }
    31. }
    32. if (even) {
    33. if (even.next) {
    34. //根据链表节点编号的奇偶性, 偶数节点的下一个节点的下一个节点是偶数
    35. even = even.next.next;
    36. } else {
    37. even = null;
    38. }
    39. }
    40. }
    41. // 剩下的为最后一个节点连接偶数节点最终返回的就是所需要的
    42. oddNode.next = targetEven.next;
    43. return targetOdd.next;
    44. };