leetcode143.重排链表

发布于:2025-08-31 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、题目描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:
在这里插入图片描述
输入:head = [1,2,3,4]
输出:[1,4,2,3]
示例 2:
在这里插入图片描述
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

提示:

链表的长度范围为 [1, 5 * 104]
1 <= node.val <= 1000

二、题目解析

1、递归法
1-》2-》3-》4-》5,预期1-》5-》2-》4-》3
若一次把首尾交换
先得到1-》5,接下来把2-》3-》4交换,第二次交换完的结果是2-》4(剩余节点3还没处理)
第一次交换后的尾节点的next指针和第二次交换后的头指针连接:
1-》5-》2-》4,
第二次交换后尾节点的next指针和第三次交换后的头指针连接:
1-》5-》2-》4-》3
当剩余1个元素或者剩余2个元素的时候为递归出口(由于代码逻辑里会存在部分处理出现空指针异常,故需要特殊处理)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public void reorderList(ListNode head) {
        if(head == null || head.next == null){
            return;
        }
        change(head);
    }
    public ListNode change(ListNode head) {
        //只有一个元素的特殊情况,不需要任何操作,直接返回头指针
        if(head.next == null){
            return head;
        }
        //nextStart、nextEnd为下轮递归调用的头指针,尾指针,为此轮调用的第二个元素,倒数第二个元素
        ListNode start = head,nextStart = head.next,nextEnd = head,end = head;
        //定位到尾节点
        while(end.next != null){
            end = end.next;
        }
        //定位到倒数第二个节点(是下轮递归的尾节点)
        while(nextEnd.next != end){
            nextEnd = nextEnd.next;
        }
        //只有两个元素的特殊情况,不需要任何操作,直接返回头指针
        if(nextEnd == start){
            return head;
        }
        head.next = end;
        nextEnd.next = null;
        end.next = change(nextStart);
        return head;
    }
}

在这里插入图片描述
2、利用反转链表(三指针法反转链表)
1-》2-》3-》4-》5,预期1-》5-》2-》4-》3

  • 平均拆成2个链表:1-》2-》3,4-》5
  • 第二个链表反转:1-》2-》3,5-》4
  • 两个链表合并:1-》5-》2-》4-》3
    代码如下:
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public void reorderList(ListNode head) {
        if(head == null || head.next == null){
            return;
        }
        //平均拆成两个链表(考虑链表奇数个或者偶数个情况)
        ListNode fast = head,slow = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode head1 = head,head2 = slow.next;
        slow.next = null;
        //反转链表2
        head2 = reverse(head2);
        //合并链表
        ListNode tail = head1;
        int count = 1;
        head1 = head1.next;
        while(head1 != null || head2 != null){
            //这句不能放在head1和head2赋值之后,因为head1和head2值会改变
            tail.next = count % 2 == 0 ? head1 : head2;
            head1 = head1 ==null || count % 2 == 1 ? head1 : head1.next;
            //注:不应用&&表示,因为不满足其中任一个,都不应该取next,否则会空指针异常
            //head2 = head2 != null && count % 2 == 0 ? head2 : head2.next;
            head2 = head2 == null || count % 2 == 0 ? head2 : head2.next;
            tail = tail.next;
            count++;
        }
        return;
    }
    public static ListNode reverse(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode prev = new ListNode();
        prev.next = head;
        ListNode cur = head,successor = head.next;
        while(cur != null){
            //testcase:1-》2-》3-》4
            //注:prev指向会变,这种判断会导致断链(失去第1个,第2个节点)
            //cur.next = prev.next == head ? null : prev;
            //注:虚拟头结点不能用null来判断(改变prev指向,头结点内存仍存在),这样判断头指针和第一个节点会形成环
            //cur.next = prev.next == null ? null : prev;
            cur.next = cur == head ? null : prev;
            prev = cur;
            cur = successor;
            //防止空指针异常
            successor = successor == null ? null : successor.next;
        }
        return prev;
    }
}

在这里插入图片描述
reverse方法优化:prev直接用null初始值,就不需要特殊判断头结点了

public static ListNode reverse(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode prev = null;
        ListNode cur = head,successor = head.next;
        while(cur != null){
            //testcase:1-》2-》3-》4
            //注:prev指向会变,这种判断会导致断链(失去第1个,第2个节点)
            //cur.next = prev.next == head ? null : prev;
            //注:虚拟头结点不能用null来判断(改变prev指向,头结点内存仍存在),这样判断头指针和第一个节点会形成环
            //cur.next = prev.next == null ? null : prev;
            cur.next =  prev;
            prev = cur;
            cur = successor;
            //防止空指针异常
            successor = successor == null ? null : successor.next;
        }
        return prev;
    }

合并链表优化:

//当第二个链表的头节点的指向空时,不进循环入
        while(newHead != null){
            //定义一个伪节点,存储第二个链表的第二节点
            ListNode temp = newHead.next;
            //将第二个链表的头节点,指向第一个链表链表的第二个节点。
            newHead.next = head.next;
            //将第一个链表的头节点,指向第二个链表的头节点。
            head.next = newHead;
            //将指向第一个链表的头节点的指针,移动到第一个链表的第二个节点
            head = newHead.next;
            //将指向第二个链表的头节点的指针,移动到第二个链表的第二个节点
            newHead = temp;
        }