【每日一题Day281】LC143 重排链表 | 快慢指针+反转链表

简介: 【每日一题Day281】LC143 重排链表 | 快慢指针+反转链表

重排链表【LC143】

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

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/reorder-list

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

最近是链表训练营

  • 2022/09/22
  • 2023/07/31
快慢指针+反转链表

类似9.回文链表*【LC234】

思路:首先找到链表的中间节点,然后将后半部分链表进行反转,然后将链表进行重组

实现

找到前半部分链表的尾节点

若链表有奇数个节点,则中间的节点看作是前半部分

计算链表节点的数量,然后遍历链表找到前半部分的尾节点

或者使用快慢指针:慢指针一次走一步,快指针一次走两步,快慢指针同时出发,当快指针移动到链表的末尾时,慢指针恰好到链表的中间;

反转后半部分链表

切断前后链表

合成链表

返回结果

/**
 * 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) {
        // 找到中间节点
        ListNode end1 = findMiddlenode(head);
        // 反转后半部分节点
        ListNode start2 = reverseList(end1.next);
        end1.next = null;
        // 拼接链表
        ListNode p1 = head, p2 = start2;
        while (p2 != null){
            ListNode tmp1 = p1.next, tmp2 = p2.next;
            p1.next = p2;
            p2.next = tmp1;
            p1 = tmp1;
            p2 = tmp2;
        }
    }
    public ListNode reverseList(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null){
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next; 
        }
        return pre;
    }
    public ListNode findMiddlenode(ListNode head){
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null){
            //  while (fast != null && fast.next != null){ 错误
            // 当链表中只有两个节点时,慢指针会移动一步
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}
  • 复杂度分析
  • 时间复杂度:O(N),其中 N是链表中的节点数。
  • 空间复杂度:O(1)
数组+双向指针

把链表放进数组中,然后通过双指针法,一前一后,来遍历数组,构造链表。

// 方法一 Java实现,使用数组存储节点
 class Solution {
    public void reorderList(ListNode head) {
        // 双指针的做法
        ListNode cur = head;
        // ArrayList底层是数组,可以使用下标随机访问
        List<ListNode> list = new ArrayList<>();  
        while (cur != null){
            list.add(cur);
            cur = cur.next;
        }
        cur = head;  // 重新回到头部
        int l = 1, r = list.size() - 1;  // 注意左边是从1开始
        int count = 0;
        while (l <= r){
            if (count % 2 == 0){
                // 偶数
                cur.next = list.get(r);
                r--;
            }else {
                // 奇数
                cur.next = list.get(l);
                l++;
            }
            // 每一次指针都需要移动
            cur = cur.next;
            count++;
        }
        // 注意结尾要结束一波
        cur.next = null;
    }
}
  • 复杂度分析
  • 时间复杂度:O(N),其中 N是链表中的节点数。
  • 空间复杂度:O(N)
双向队列

把链表放进双向队列,然后通过双向队列一前一后弹出数据,来构造新的链表。这种方法比操作数组容易一些,不用双指针模拟一前一后了

// 方法二:使用双端队列,简化了数组的操作,代码相对于前者更简洁(避免一些边界条件)
class Solution {
    public void reorderList(ListNode head) {
        // 使用双端队列的方法来解决
        Deque<ListNode> de = new LinkedList<>();
        // 这里是取head的下一个节点,head不需要再入队了,避免造成重复
        ListNode cur = head.next;  
        while (cur != null){
            de.offer(cur);
            cur = cur.next;
        }
        cur = head;  // 回到头部
        int count = 0;
        while (!de.isEmpty()){
            if (count % 2 == 0){
                // 偶数,取出队列右边尾部的值
                cur.next = de.pollLast();
            }else {
                // 奇数,取出队列左边头部的值
                cur.next = de.poll();
            }
            cur  = cur.next;
            count++;
        }
        cur.next = null;
    }
}
  • 复杂度分析
  • 时间复杂度:O(N),其中 N是链表中的节点数。
  • 空间复杂度:O(1)
目录
相关文章
|
6月前
|
索引
【力扣刷题】两数求和、移动零、相交链表、反转链表
【力扣刷题】两数求和、移动零、相交链表、反转链表
49 2
【力扣刷题】两数求和、移动零、相交链表、反转链表
|
24天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
29 1
|
1月前
链表指针的传参,传值和传地址
本文讨论了链表操作中指针传参的问题,特别是指针的传值与传地址的区别,并提供了修正代码,以确保链表插入操作能正确地修改指针指向的地址。
17 1
链表指针的传参,传值和传地址
|
6月前
|
存储 C语言
用指针处理链表
用指针处理链表
60 3
|
1月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
24 0
|
4月前
【数据结构OJ题】复制带随机指针的链表
力扣题目——复制带随机指针的链表
53 1
【数据结构OJ题】复制带随机指针的链表
|
6月前
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点.
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点
53 1
|
5月前
|
算法
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
|
6月前
|
C++
[leetcode 链表] 反转链表 vs 链表相交
[leetcode 链表] 反转链表 vs 链表相交
|
6月前
|
存储 C语言
链表—初始化指针变和创建新的节点------区别应用分析
链表—初始化指针变和创建新的节点------区别应用分析