赞
踩
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3] 输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0] 输出:[-1,0,3,4,5]
示例 3:
输入:head = [] 输出:[]
提示:
[0, 5 * 104]
内-105 <= Node.val <= 105
进阶:你可以在 O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
大体实现:
基本情况处理:首先检查链表是否为空或只有一个节点。如果是,那么它已经是有序的,直接返回该链表。
分割链表:使用快慢指针技术找到链表的中点,并将链表从中点处分割成两个子链表。快指针fast
每次移动两步,慢指针slow
每次移动一步,同时用一个prevPtr
指针来记录slow
的前一个节点,以便在找到中点后将链表分割。
递归排序:对分割后的两个子链表递归地调用sortList
方法进行排序。由于递归的性质,这两个子链表最终都会被分割成更小的子链表,直到每个子链表只包含一个节点(或为空),然后这些子链表会被合并成有序链表。
合并链表:使用merge
方法将两个已排序的子链表合并成一个有序链表。合并过程中,通过比较两个链表头节点的值,将较小的节点连接到结果链表的末尾,并移动相应的指针。当其中一个链表遍历完时,将另一个链表的剩余部分直接连接到结果链表的末尾。
细节解释
快慢指针:快指针fast
每次移动两步,慢指针slow
每次移动一步。当快指针到达链表末尾时,慢指针正好位于链表的中点(或中点的前一个节点,如果链表长度为奇数)。prevPtr
用于记录slow
的前一个节点,以便在找到中点后将链表分割。
链表分割:通过将prevPtr.next
设置为null
,可以将链表从中点处分割成两个子链表。prevPtr
是slow
的前一个节点,因此prevPtr.next = null
会将slow
及其后面的节点与前面的节点断开连接。(在使用快慢指针找到中点并分割链表时,需要确保链表长度至少为2,否则fast.next.next
可能会引发NullPointerException
。但在这个实现中,由于首先检查了链表是否为空或只有一个节点,所以这种情况不会发生。)
递归调用:对分割后的两个子链表(head
和slow
)
- public class Solution {
- // 主函数,用于对链表进行排序
- public ListNode sortList(ListNode head) {
- // 如果链表为空或只有一个节点,则它已经是有序的,直接返回
- if (head == null || head.next == null) {
- return head;
- }
-
- // 使用快慢指针找到链表的中点
- ListNode slow = head; // 慢指针,每次移动一步
- ListNode fast = head; // 快指针,每次移动两步
- ListNode prevPtr = null; // 用于记录slow的前一个节点,以便分割链表
- while (fast != null && fast.next != null) {
- prevPtr = slow;
- slow = slow.next;
- fast = fast.next.next;
- }
-
- // 分割链表:将链表从中间断开,prevPtr.next原本指向slow,现在设置为null
- prevPtr.next = null;
-
- // 递归地对左右两部分进行排序
- // 注意:这里的left是原链表的左半部分,而right是原链表的右半部分(从slow开始)
- ListNode left = sortList(head); // 对左半部分排序
- ListNode right = sortList(slow); // 对右半部分排序
-
- // 合并两个有序链表
- // 返回合并后的链表头节点
- return merge(left, right);
- }
-
- // 合并两个有序链表的辅助函数
- private ListNode merge(ListNode l1, ListNode l2) {
- // 创建一个哑节点,方便处理链表头部
- ListNode dummy = new ListNode(0);
- ListNode tail = dummy; // tail用于遍历并连接合并后的链表
-
- // 遍历两个链表,按序连接节点
- while (l1 != null && l2 != null) {
- if (l1.val < l2.val) {
- tail.next = l1;
- l1 = l1.next;
- } else {
- tail.next = l2;
- l2 = l2.next;
- }
- tail = tail.next; // 移动tail到当前连接的节点
- }
-
- // 如果l1或l2还有剩余节点,直接将剩余部分连接到tail后面
- if (l1 != null) {
- tail.next = l1;
- }
- if (l2 != null) {
- tail.next = l2;
- }
-
- // 返回合并后的链表(跳过哑节点)
- return dummy.next;
- }
- }
分别递归调用sortList
方法进行排序。注意,这里的head
是原链表的头节点,而slow
是分割后右子链表的头节点。
合并链表:使用merge
方法将两个已排序的子链表合并成一个有序链表。合并过程中,通过比较两个链表头节点的值,将较小的节点连接到结果链表的末尾,并移动相应的指针。当其中一个链表遍历完时,另一个链表的剩余部分(如果有的话)将直接连接到结果链表的末尾。
- public class Solution {
- // 主函数,用于对链表进行排序
- public ListNode sortList(ListNode head) {
- // 如果链表为空或只有一个节点,则它已经是有序的,直接返回
- if (head == null || head.next == null) {
- return head;
- }
-
- // 使用快慢指针找到链表的中点
- ListNode slow = head; // 慢指针,每次移动一步
- ListNode fast = head; // 快指针,每次移动两步
- ListNode prevPtr = null; // 用于记录slow的前一个节点,以便分割链表
- while (fast != null && fast.next != null) {
- prevPtr = slow;
- slow = slow.next;
- fast = fast.next.next;
- }
-
- // 分割链表:将链表从中间断开,prevPtr.next原本指向slow,现在设置为null
- prevPtr.next = null;
-
- // 递归地对左右两部分进行排序
- // 注意:这里的left是原链表的左半部分,而right是原链表的右半部分(从slow开始)
- ListNode left = sortList(head); // 对左半部分排序
- ListNode right = sortList(slow); // 对右半部分排序
-
- // 合并两个有序链表
- // 返回合并后的链表头节点
- return merge(left, right);
- }
-
- // 合并两个有序链表的辅助函数
- private ListNode merge(ListNode l1, ListNode l2) {
- // 创建一个哑节点,方便处理链表头部
- ListNode dummy = new ListNode(0);
- ListNode tail = dummy; // tail用于遍历并连接合并后的链表
-
- // 遍历两个链表,按序连接节点
- while (l1 != null && l2 != null) {
- if (l1.val < l2.val) {
- tail.next = l1;
- l1 = l1.next;
- } else {
- tail.next = l2;
- l2 = l2.next;
- }
- tail = tail.next; // 移动tail到当前连接的节点
- }
-
- // 如果l1或l2还有剩余节点,直接将剩余部分连接到tail后面
- if (l1 != null) {
- tail.next = l1;
- }
- if (l2 != null) {
- tail.next = l2;
- }
-
- // 返回合并后的链表(跳过哑节点)
- return dummy.next;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。