一刷173-力扣热题-148排序链表(m)(剑指 Offer II 077. 链表排序)

题目:
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

在这里插入图片描述

输入:head = [4,2,1,3]
输出:[1,2,3,4]

在这里插入图片描述

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

输入:head = []
输出:[]

提示:
链表中节点的数目在范围 [0, 5 * 104]-105 <= Node.val <= 105
------------------------------
思考:
归并排序(递归法)
题目要求时间空间复杂度分别为O(nlogn)O(1),
根据时间复杂度我们自然想到二分法,从而联想到归并排序;

对数组做归并排序的空间复杂度为 O(n),
分别由新开辟数组O(n)和递归函数调用O(logn)组成,而根据链表特性:

数组额外空间:链表可以通过修改引用来更改节点顺序,无需像数组一样开辟额外空间;
递归额外空间:递归调用函数将带来O(logn)的空间复杂度,
因此若希望达到O(1)空间复杂度,则不能使用递归。

通过递归实现链表归并排序,有以下两个环节:
分割 cut 环节:
找到当前链表中点,并从中点将链表断开(以便在下次递归 cut 时,链表片段拥有正确边界);
我们使用 fast,slow 快慢双指针法,奇数个节点找到中点,偶数个节点找到中心左边的节点。
找到中点 slow 后,执行 slow.next = None 将链表切断。
递归分割时,输入当前链表左端点 head 和中心节点 slow 的下一个节点 tmp(因为链表是从 slow 切断的)
cut 递归终止条件: 当head.next == None时,说明只有一个节点了,直接返回此节点。

合并 merge 环节: 
将两个排序链表合并,转化为一个排序链表。
双指针法合并,建立辅助ListNode h 作为头部。
设置两指针 left, right 分别指向两链表头部,
比较两指针处节点值大小,由小到大加入合并链表头部,指针交替前进,直至添加完两个链表。
返回辅助ListNode h 作为头部的下个节点 h.next。
时间复杂度 O(l + r),l, r 分别代表两个链表长度。
当题目输入的 head == None 时,直接返回None

在这里插入图片描述

/**
 * 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 ListNode sortList(ListNode head) {
        if (head == null || head.next == null) return head;//边缘性监测
        ListNode slow = head; //定义快慢指针
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {//! next next.next
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode tmp = slow.next;//定义节点表示: 第一个对半拆分后的第二个链表的头节点
        slow.next = null;//对半拆分链表
        ListNode left = sortList(head);//左链表 递归拆分
        ListNode right = sortList(tmp);//右链表 递归拆分
        ListNode h = new ListNode(0); //定义一个返回结果的伪节点的指针
        ListNode res = h;
        while (left != null && right != null) {// !! left right 都不能为空
            if (left.val < right.val) {
               res.next = left;
                left = left.next;
            }else {
                res.next = right;
                right = right.next;
            }
            res = res.next;
        }
       res.next = left != null ? left : right;//若左链表有剩余元素,就拼接左链表,否则拼接右链表
        return h.next;
    }
}

LC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值