Leetcode刷题之链表

394 阅读17分钟

160. 相交链表(Easy)

编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表:

在节点 c1 开始相交。


示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。


示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

解法一:两个单链表有交点,则说明从交点之后的链表都是同一段,那么我们可以用两个指针,在两条链表的同个点同时出发,第一个相同点的时候就是第一个交点。

  • 先遍历两条单链表,得到他们的长度
  • 让较长的单链表先走|lenA-lenB|步,然后两者同步开始走
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null){
            return null;
        }

        //统计两条单链表的长度,并判断是否相交
        //如果相交,那么最后一个ListNode相等
        int countA = 0;
        int countB = 0;
        int k = 0;
        ListNode A = headA;
        ListNode B = headB;
        while (A.next != null){
            countA++;
            A = A.next;
        }
        while (B.next != null){
            countB++;
            B = B.next;
        }
        if (A != B){
            return null;
        }
        
        A = headA;
        B = headB;
        //让长链表先走长度之差步
        if (countA <= countB){
            k = countB - countA;
            //长的先走
            for (int i = 0; i < k; i++) {
                B = B.next;
            }
            //然后两者一起走
            while (A != B){
                A = A.next;
                B = B.next;
            }
            return A;
        }else {
            k = countA - countB;
            //长的先走
            for (int i = 0; i < k; i++) {
                A = A.next;
            }
            //然后两者一起走
            while (A != B){
                A = A.next;
                B = B.next;
            }
            return B;
        }
    }
}

方法二:链表A长度a+c(c为公共部分长度),链表B长度b+c,那么a+c+b=b+c+a

  • 如果存在交点,那么当访问 A链表的指针访问到链表尾部时,令它从链表 B的头部开始访问链表 B;(a+c+b的过程)
  • 同样地,当访问 B链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。(b+c+a的过程)
  • 这样就能控制访问 A 和 B两个链表的指针能同时访问到交点。
public class Solution {
    public class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode A = headA;
        ListNode B = headB;
        //A从链表A出发->链表B
        //B从链表B出发->链表A
        while (A != B){
            //A先走完链表A走B;B反过来
            A = (A == null) ? headB : A.next;
            B = (B == null) ? headA : B.next;
        }
        return A;
    }
}


206. 反转链表(Easy)

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?


递归解法:递归解法理解起来比较困难,我在看了几位大佬的解答之后才能理解。

  • 首先需要先知道递归是怎么解决一个问题的,和一般解答不同的是:递归是先从顶部递归到底部才开始解决问题,然后至下而上。总的过程是由上到下->由下到上
  • 理解了这个过程之后再去看解答过程就比较容易理解了。借用题解区一位大佬的图
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //递归的终止条件
        if (head == null || head.next == null){
            return head;
        }

        //递归到最后一个节点(cur)
        ListNode cur = reverseList(head.next);
        
        //这个翻转过程可以借鉴上面的动图配合理解
        //链表是1->2->3->4->5,此时cur就是5,而head是4
        //我们的目标是从4->5变成4<-5
        // head.next.next = head 相当于 5.next=4
        head.next.next = head;
        
        //这里的head.next设置为空是为了防止链表循环
        head.next = null;
        
        //返回翻转之后的头节点
        return cur;
    }
}

迭代解法:这种解法应该是我们最熟悉的一种,也是比较简单的,直接使用三个变量就可以解决。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //准备一个空节点当做前节点,tmp存储当前节点的下一节点
        ListNode pre = null;
        ListNode cur = head;
        ListNode tmp = null;
        
        while (cur != null){
            //存储下一节点,以防更改指针之后丢失下一节点
            tmp = cur.next;
            //翻转
            cur.next = pre;
            //cur和pre都前进一步
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}


21. 合并两个有序链表(Easy)

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4


迭代解法:大概思路是先准备一个新链表,然后比较两单链表,小的加进去。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    ublic ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //如果其中一条单链表为空,直接返回另外一条
        if (l1 == null){
            return l2;
        }
        if (l2 == null){
            return l1;
        }

        //准备一条空链表
        ListNode head = new ListNode(-1);
        ListNode cur = head;
        //比较两链表
        while (l1 != null && l2 != null){
            //小的添加进去
            if (l1.val <= l2.val){
                cur.next = l1;
                l1 = l2.next;
            }else {
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        //两条链表长度可能不一致,剩下的加到新链表末尾
        cur.next = (l1 != null) ? l1 : l2;
        return head.next;
    }
}

递归解法:一般链表题目都可以用递归解决,用递归解答时,要先明确递归函数的定义,mergeTwoLists的定义是返回递归之后的新链表头节点,比较两条链表,小的那个next指针指向mergeTwoLists

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //如果其中一条单链表为空,直接返回另外一条
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }

        //比较两条链表,小的next指针接递归函数,并返回拼接好的
        if (l1.val <= l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}


83. 删除排序链表中的重复元素(Easy)

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2

示例 2:

输入: 1->1->2->3->3
输出: 1->2->3


迭代解法:准备一个指针cur,遍历链表,当前节点cur等于下一节点next的话,删除下一节点next。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode cur = head;
        //遍历单链表
        while (cur != null && cur.next != null){
            if (cur.val == cur.next.val){
                cur.next = cur.next.next;
            }else {
                cur = cur.next;
            }
        }
        return head;
    }
}

递归解法:同样地,这道题也有递归解法,依然先明确递归函数定义:deleteDuplicates代表的是删除重复节点之后单链表的头节点

class Solution{
    public ListNode deleteDuplicates(ListNode head) {
        //base case
        if (head == null || head.next == null) {
            return head;
        }
        
        // heda.next连接的是已经删除重复节点之后的单链表
        head.next = deleteDuplicates(head.next);
        // 此时,head后面的链表已经去重,只需要判断head和head.next是否重复即可
        return head.val == head.next.val ? head.next : head;
    }
}


19. 删除链表的倒数第N个节点(Medium)

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

  • 给定的 n 保证是有效的。

进阶:

  • 你能尝试使用一趟扫描实现吗?

解法一:可以先遍历一趟链表,得出链表长度len,第二遍的时候走len-n-1步得到倒数第n个节点的前一个节点,然后删除倒数第n个即可

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //统计链表长度
        int len = 0;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = head;
        while (cur != null){
            cur = cur.next;
            len++;
        }

        //指针走len-n步,得到的就是倒数第n个
        cur = dummy;
        for (int i = 0; i < len - n; i++) {
            cur = cur.next;
        }
        //跳过被删除的元素
        cur.next = cur.next.next;
        return dummy.next;
    }
}

解法二:使用快慢指针

  • 让快指针先走n步,如果此时快指针值为null,那么说明要删除的为首节点,返回head.next即可
  • 不为空的话就两个指针继续走,快指针走到末尾时,慢指针指向要删除节点的前一个,删除即可
class Soution{
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head == null){
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        //快指针先走n步
        for (int i = 0; i < n; i++) {
            fast = fast.next;
        }
        //如果为空,则说明要删除的为首节点
        if (fast == null){
            return head.next;
        }
        
        while (fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        //删除节点
        slow.next = slow.next.next;
        return head;
    }
}


24. 两两交换链表中的节点(Medium)

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.


递归解法:参考了题解区一位大佬的解答

  • 递归的解法和迭代解法的区别是递归关注的是当前的处理过程,因为递归的本质就是重复,而迭代关注的是整个过程
  • 关于递归我们需要关注3点
    1. 返回值
    2. 调用单元做了什么
    3. 终止条件
  • 在本题中:
    1. 返回值:交换完成的子链表
    2. 调用单元:设需要交换的两个点为 head 和 next,head 连接后面交换完成的子链表,next 连接 head,完成交换
    3. 终止条件:head 为空指针或者 next 为空指针,也就是当前无节点或者只有一个节点,无法进行交换
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        //只有一个节点无法进行交换,直接返回
        if (head == null || head.next == null){
            return head;
        }
        //交换
        ListNode next = head.next;
        head.next = swapPairs(head.next);
        next.next = head;
        //返回交换之后的头节点
        return next;
    }
}

迭代解法:基本思路和递归解法相似,迭代解法需要准备一个dummy节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        //准备一个dummy节点
        ListNode pre = new ListNode(0);
        pre.next = head;
        //准备cur节点
        ListNode cur = pre;
        //遍历链表,进行交换
        while (cur.next != null && cur.next.next != null){
            ListNode l1 = cur.next;
            ListNode l2 = cur.next.next;
            //交换
            l1.next = l2.next;
            l2.next = l1;
            cur.next = l2;
            //将cur往后移
            cur = l1;
        }
        return pre.next;
    }
}


445. 两数相加 II(Medium)

给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:

  • 如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。

示例:

输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 8 -> 0 -> 7


解法一:数字最高位置链表起始位置,所以我们做加法的时候需要将链表翻转过来,可以考虑使用“栈”来存储,弹出来的时候顺序就是相反的。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //将两条链表存储进栈
        Stack<Integer> stack1 = buildStack(l1);
        Stack<Integer> stack2 = buildStack(l2);
        //准备新链表的dummy节点
        ListNode head = new ListNode(-1);
        //进位
        int carry = 0;
        while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0){
            //当前位=x+y+carry
            int x = stack1.isEmpty() ? 0 : stack1.pop();
            int y = stack2.isEmpty() ? 0 : stack2.pop();
            int sum = x + y + carry;
            ListNode node = new ListNode(sum % 10);
            //更新进位
            carry = sum / 10;
            //连接顺序要符合链表头部为数字高位的顺序
            //新更新的节点后面接的是上次链表的头节点
            node.next = head.next;
            //更新头节点
            head.next = node;
        }
        return head.next;
    }
}

解法二:这道题还可以使用递归解法,递归实质上也是一种栈,也就可以实现从后往前取数字,我们首先统计出两个链表长度,然后根据长度来调用递归函数。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    int flow = 0;

    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        ListNode res1 = l1, res2 = l2;
        
        //统计两链表的长度
        int len1 = 0, len2 = 0;
        while (l1 != null) {
            len1++;
            l1 = l1.next;
        }
        while (l2 != null) {
            len2++;
            l2 = l2.next;
        }
        
        //根据长度之差判断哪个链表为辅助链表
        ListNode res = len1 > len2 ? add(res1, res2, len1, len2) : add(res2, res1, len2, len1);
        if (flow == 1) {
            res1 = new ListNode(1);
            res1.next = res;
            return res1;
        }
        return res;
    }

    public ListNode add(ListNode l1, ListNode l2, int len1, int len2) {
        //base case
        if (l1 == null) {
            return null;
        }
        
        //复用l1,也可以选择不复用,复用是为了进一步优化
        int temp;
        if (len1 > len2) {
            temp = l1.val;
            l1.next = add(l1.next, l2, len1 - 1, len2);
            l1.val = (temp + flow) % 10;
            flow = (temp + flow) / 10;
            return l1;
        }
        l1.next = add(l1.next, l2.next, len1 - 1, len2 - 1);
        temp = l1.val;
        l1.val = (temp + flow + l2.val) % 10;
        flow = (temp + flow + l2.val) / 10;
        return l1;
    }
}


234. 回文链表(Easy)

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

进阶:

  • 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解法一:回文链表的特点是中心对称,即顺着和反着是一样的,所以可以使用栈将节点装进去,然后弹出来一一比较

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        Stack<Integer> stack = new Stack<>();
        ListNode cur = head;
        //stack存储链表
        while (cur != null){
            stack.push(cur.val);
            cur = cur.next;
        }

        //比较stack和单链表
        while (!stack.isEmpty()){
            int num = stack.pop();
            if (num != head.val){
                return false;
            }
            head = head.next;
        }
        
        return true;
    }
}

解法二:进阶要求空间复杂度为O(1),那么就不能使用栈。可以想到既然回文结构是中心对称,那么可以将链表的后半部分翻转过来比较即可

  • 首先找到后半段的起点,有两种方法
    • 一是遍历链表,得到链表长度len,然后准备一个指针走len/2步就是后半段起点
    • 二是用“快慢”指针,快指针一次走两步,慢指针一次走一步,慢指针从head出发,快指针从head.next出发,最后慢指针停下来的点就是后半段起点
  • 将后半段翻转,逐一比较
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null){
            return true;
        }
        //准备快慢指针
        //链表个数为偶数的话,slow最后到前半段最后一个,奇数的话在正中间
        ListNode slow = head;
        ListNode fast = head.next;
        while (fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }

        //复用fast,将fast成为右半部分的第一个节点
        fast = slow.next;
        //将左边链表断开
        slow.next = null;
        //将右半部分翻转
        ListNode pre = null;
        while (fast != null){
            //保存下个节点
            ListNode next = fast.next;
            //翻转
            fast.next = pre;
            //将节点往下移
            pre = fast;
            fast = next;
        }

        //比较左右两部分,pre在右段头部分
        slow = head;
        while (slow != null && pre != null){
            if (slow.val != pre.val){
                return false;
            }
            slow = slow.next;
            pre = pre.next;
        }
        return true;
    }
}


725. 分割链表(Medium)

给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。 每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。

这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。 返回一个符合上述规则的链表的列表。

举例: 1->2->3->4, k = 5 // 5 结果 [ [1], [2], [3], [4], null ]

示例 1:

输入:
root = [1, 2, 3], k = 5
输出: [[1],[2],[3],[],[]]
解释: 输入输出各部分都应该是链表,而不是数组。
例如, 输入的结点 root 的 val= 1, root.next.val = 2, \root.next.next.val = 3, 且 root.next.next.next = null。
第一个输出 output[0] 是 output[0].val = 1, output[0].next = null。
最后一个元素 output[4] 为 null, 它代表了最后一个部分为空链表。

示例 2:

输入:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
输出: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过1.前面部分的长度大于等于后面部分的长度。

提示:

  • root 的长度范围: [0, 1000].
  • 输入的每个节点的大小范围:[0, 999].
  • k 的取值范围: [1, 50].

题解:

  • 题目让我们尽量把链表均分成k份,那么我们需要知道链表的长度len,然后len除于k,得到size份,len对k取余,得到最后剩余的mod个是多出来的,加到每一份里面。
  • 接着遍历链表,将链表分成k份
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode[] splitListToParts(ListNode root, int k) {
        //统计链表长度
        int len = 0;
        ListNode cur = root;
        while (cur != null) {
            len++;
            cur = cur.next;
        }

        //每1份节点 = 商大小的节点 + 从余数里取出1个节点
        //取余
        int mod = len % k;
        //求商
        int size = len / k;

        //将链表分成k份
        ListNode[] listNodes = new ListNode[k];
        cur = root;
        //分成k份
        for (int i = 0; cur != null && i < k; i++) {
            listNodes[i] = cur;
            //有些份是没有多余的节点的
            int curSize = size + (mod-- > 0 ? 1 : 0);
            //添加curSize个节点
            for (int j = 0; j < curSize - 1; j++) {
                cur = cur.next;
            }
            //断开当前份与后面份
            ListNode next = cur.next;
            cur.next = null;
            //后移节点
            cur = next;
        }
        return listNodes;
    }
}


328. 奇偶链表(Medium)

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 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

说明:

  • 应当保持奇数节点和偶数节点的相对顺序。
  • 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。

题解:准备奇偶指针,分别指向奇偶其实位置,用一个指针来保存偶数指针其实位置,把奇节点的指向偶节点的下一个(一定是奇节点),此奇节点后移一步,再把偶节点指向下一个奇节点的下一个(一定是偶节点),此偶节点后移一步,以此类推直至末尾,此时把分开的偶节点的链表连在奇节点的链表后即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode oddEvenList(ListNode head) {
        if (head == null){
            return null;
        }
        //准备奇偶指针和保存偶数起点位置的指针
        ListNode odd = head;
        ListNode even = head.next;
        ListNode evenHead = even;
        
        //聚集奇偶链表元素
        while (even != null && even.next != null){
            //连接奇数元素,然后后移
            odd.next = odd.next.next;
            odd = odd.next;
            even.next = even.next.next;
            even = even.next;
        }
        odd.next = evenHead;
        return head;
    }
}