【Leetcode】148. 排序链表

该篇博客详细介绍了如何使用归并排序算法对链表进行排序,通过寻找链表中点并将其拆分为两半,分别进行递归排序,然后合并两个已排序的链表。博主分享了三种不同的实现方式,所有方法都在O(nlogn)时间复杂度和常数级空间复杂度下完成。代码执行效率高,击败了大部分在线提交的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

在这里插入图片描述

// 148. 排序链表

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

// 进阶:
// 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

题解

/**
 * 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; }
 * }
 */
 
 
// 求链表中点 + 合并排序链表
// 这道题需要结合一下【Leetcode】876. 链表的中间结点 和
// 【剑指offer】25. 合并两个排序的链表 这两道题的方法。
// 
// 我们的思路是将链表按照归并排序的思路,将链表拆成left和right左右两半,
// 再将left和right左右两半各自拆成左右两半,一直拆解到left只有一个结点,
// right也只有一个结点。我们用 25. 合并两个排序的链表 的方法,将它们合并
// 得到排序的小链表,两个排序的小链表合并成两个排序的大链表,两个排序
// 的大链表最后层层合并,就合并回了原链表,我们就得到了排序好的链表。


// 首先定义middleNode函数,
// 我们需要拆链表为left right左右两半,就需要取链表中点,
// 这里和【Leetcode】876. 链表的中间结点基本一样,有一个小trick。
// 由于我们不只是需要找中点,还需要把left和right拆开。而【Leetcode】876
// 中找到的是链表中点(奇数个元素)或者链表中间位置靠右的点(偶数个元素)
// 。我们需要切断链表left和right,需要找链表中点的前一个点middlePre,然后
// 令middlePre = null就切断了left和right。为了找到链表中点的前一个点,
// 我们将快慢针的快针初始化到head.next的位置,然后再快慢针遍历,这样慢针
// 找到的就是中点的前一个点。
// 
// 定义mergeNodeList函数,
// 这里和【剑指offer】25. 合并两个排序的链表 一模一样的。
// 
// 最后处理主函数sortList,
// sortList的功能是将输入的链表head拆分为left和right左右两半,然后各自将
// left和right进行排序,再将left和right合并。
// 我们定义递归终止条件为head为null或者head.next为null,则直接返回head。
// 因为如果head为null或者只有一个结点,那就无法拆分为左右两半。
// 然后调用middleNode找head的中点的前一个点middlePre,我们记录middlePre
// 的下一个点作为右半部分链表的头结点rightHead,然后将左右切断,即
// middlePre.next = null。这样就切断了left和right,然后我们将left递归
// 调用sortList,将left也要拆分排序后归并。将right递归调用sortList,
// 将right也要拆分排序后归并。
// 最后函数返回:合并两个排序的左右子链表即可,mergeNodeList(left, right)。
// 
// 执行用时:6 ms, 在所有 Java 提交中击败了98.94%的用户
// 内存消耗:42.9 MB, 在所有 Java 提交中击败了97.93%的用户
class Solution {
    public ListNode sortList(ListNode head) {
		if (head == null || head.next == null)
			return head;
		
		ListNode middlePre = middleNode(head);
		ListNode rightHead = middlePre.next;
		middlePre.next = null;
		ListNode left = sortList(head);
		ListNode right = sortList(rightHead);
		
		return mergeNodeList(left, right);
    }
	
	public ListNode middleNode(ListNode head) {
        if (head == null)
            return null;
		ListNode cur = head.next;
        ListNode pre = head;
        while (cur != null && cur.next != null) {
            cur = cur.next.next;
            pre = pre.next;
        }
        return pre;
    }
	
	public ListNode mergeNodeList(ListNode l1, ListNode l2) {
		if (l1 == null)
			return l2;
		else if (l2 == null)
			return l1;
			
		ListNode res;
		ListNode temp;
		res = (l1.val <= l2.val) ? l1 : l2;
		while (l1 != null && l2 != null) {
			if (l1.val <= l2.val) {
				while (l1.next != null && l1.next.val <= l2.val)
					l1 = l1.next;
				temp = l1;
				l1 = l1.next;
				temp.next = l2;
			}
			else {
				while (l2.next != null && l2.next.val < l1.val)
					l2 = l2.next;
				temp = l2;
				l2 = l2.next;
				temp.next = l1;
			}
		}
		return res;
	}
}




// 执行用时:6 ms, 在所有 Java 提交中击败了98.96%的用户
// 内存消耗:46.9 MB, 在所有 Java 提交中击败了28.04%的用户
class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null)
            return head;
        ListNode mid = middle(head);  // 找中点,准备拆成两半
        ListNode left = head;  // 左半链表
        ListNode right = mid.next;  // 右半链表
        mid.next = null;  // 左右半边拆开
        left = sortList(left);
        right = sortList(right);
        ListNode res = mergeTwoLists(left, right);
        return res;
    }

    // 找链表中点(中点靠左)
    private ListNode middle(ListNode head) {
        if (head == null || head.next == null)
            return head;
        ListNode pre = head;
        ListNode cur = head.next;
        while (cur != null && cur.next != null) {
            pre = pre.next;
            cur = cur.next.next;
        }
        return pre;
    }

    // 合并排序链表
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) 
            return l2;
        else if (l2 == null)
            return l1;
        
        ListNode dummyHead = new ListNode(-1);
        ListNode head = dummyHead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                head.next = l1;
                head = l1;
                l1 = l1.next;
            }
            else {
                head.next = l2;
                head = l2;
                l2 = l2.next;
            }
        }
        if (l1 != null)
            head.next = l1;
        else if (l2 != null)
            head.next = l2;
        return dummyHead.next;
    }
}

////////////////////////////////////////////////////////

// 你也可以选择把所有的方法全部揉在一起。
// 执行用时:5 ms, 在所有 Java 提交中击败了99.52%的用户
// 内存消耗:43.2 MB, 在所有 Java 提交中击败了94.07%的用户
class Solution {
    public ListNode sortList(ListNode head) {
		if (head == null || head.next == null)
			return head;
		

		ListNode cur = head.next;
        ListNode pre = head;
        while (cur != null && cur.next != null) {
            cur = cur.next.next;
            pre = pre.next;
        }


        ListNode rightHead = pre.next;
		pre.next = null;
		ListNode left = sortList(head);
		ListNode right = sortList(rightHead);
		

		ListNode res;
		ListNode temp;
		res = (left.val <= right.val) ? left : right;
		while (left != null && right != null) {
			if (left.val <= right.val) {
				while (left.next != null && left.next.val <= right.val)
					left = left.next;
				temp = left;
				left = left.next;
				temp.next = right;
			}
			else {
				while (right.next != null && right.next.val < left.val)
					right = right.next;
				temp = right;
				right = right.next;
				temp.next = left;
			}
		}
		return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锥栗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值