143. 重排链表

357 阅读2分钟

题目介绍

力扣143题:leetcode-cn.com/problems/re…

image.png

image.png

解法一 存储

链表的缺点就是不能随机存储,当我们想取末尾元素的时候,只能从头遍历一遍,很耗费时间。第二次取末尾元素的时候,又得遍历一遍。

所以先来个简单粗暴的想法,把链表存储到线性表中,然后用双指针依次从头尾取元素即可。

代码如下:

/**
 * 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;
        }

        List<ListNode> nodeList = new ArrayList<>();
        ListNode temp = head;    
        while(temp != null) {
            nodeList.add(temp);
            temp = temp.next;
        }

        int left = 0;//左指针
        int right = nodeList.size() - 1;//右指针
        while(left < right) {
            ListNode leftNode = nodeList.get(left);
            ListNode rightNode = nodeList.get(right);
            if(leftNode.next == rightNode) {
                //节点个数为偶数个时,将中间靠右节点的next指针置为空,防止链表构成环
                rightNode.next = null;
            }else {
                rightNode.next = leftNode.next;
                leftNode.next = rightNode;
            }
            left++;
            right--;
        }

        if(left == right) {
            ListNode leftNode = nodeList.get(left);
            //节点个数为奇数个时,将中间节点的next指针置为空,防止链表构成环
            leftNode.next = null;
        }
    }
}

复杂度分析

  • 时间复杂度:O(N),其中 N 是链表中的节点数。
  • 空间复杂度:O(N),其中 N 是链表中的节点数。主要为线性表的开销。

方法二:寻找链表中点 + 链表逆序 + 合并链表

注意到目标链表即为将原链表的左半端和反转后的右半端合并后的结果。

这样我们的任务即可划分为三步:

  • 找到原链表的中点。我们可以使用快慢指针来 O(N) 地找到链表的中间节点。

  • 将原链表的右半端反转。我们可以使用迭代法实现链表的反转。

  • 将原链表的两端合并。

代码如下:

// 找中点+反转后半部分+合并前后两部分
public void reorderList(ListNode head) {
    if(head==null || head.next==null || head.next.next==null) {
        return;
    }
    
    // 1. 找中点,让slow指向中点,或左中点位置
    ListNode slow = head, fast = head.next;
    while (fast!=null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }

    // 2. 断开中点,反转后半部分
    ListNode head2 = null, next = slow.next;
    slow.next = null;
    slow = next;
    while(slow != null) {
        next = slow.next;
        slow.next = head2;
        head2 = slow;
        slow = next;
    }

    // 3. 合并链表head和head2
    ListNode curr = head;
    ListNode curr2 = head2;
    while(curr != null && curr2 != null) {
        next = curr.next;
        curr.next = curr2;
        curr2 = curr2.next;
        curr.next.next = next;
        curr = next;
    }
}

复杂度分析

  • 时间复杂度:O(N),其中 N 是链表中的节点数。
  • 空间复杂度:O(1)。