Leetcode 148 Sort List

Leetcode 148 Sort List

题目

Given the head of a linked list, return the list after sorting it in ascending order.

Follow up: Can you sort the linked list in O(n logn) time and O(1) memory (i.e. constant space)?

Example 1:
Input: head = [4,2,1,3]
Output: [1,2,3,4]

Example 2:
Input: head = [-1,5,3,4,0]
Output: [-1,0,3,4,5]

Example 3:
Input: head = []
Output: []

Constraints:
The number of nodes in the list is in the range [0, 5 * 104].
-105 <= Node.val <= 105

思路

题目提示要 O ( n l o g n ) O(nlogn) O(nlogn),很自然的想到用merge sort。
因为题给的数据结构是链表,不像数组可以直接除2就找到中间的位置分开,所以第一步要做的是找到分割点,这里使用的是快慢指针,一个走到底的时候,另外一个走到一半,我使用的停止条件是不满足fast.next != null && fast.next.next != null,举两个例子:

链表长度为5(奇数):
快指针:1 -> 3 -> 5
慢指针:1 -> 2 -> 3
期望分成:3+2个节点(slow.next为第二个链表起点)或2+3个节点(slow为第二个链表起点)都可以

链表长度为6(偶数):
快指针:1 -> 3 -> 5
慢指针:1 -> 2 -> 3
期望分成:3+3个节点(slow.next为第二个链表起点)

所以使用slow.next为第二个链表的起点可以统一以上两种情况。

回到上面为什么停止条件要放fast.next != null && fast.next.next != null,首先我们知道代码更新时有fast = fast.next.next,所以停止条件一定不是不满足fast != null(最后一点可能产生空指针),那为什么不选择fast.next != null,再次考虑上面两个例子。

链表长度为5(奇数):
快指针:1 -> 3 -> 5
慢指针:1 -> 2 -> 3
期望分成:3+2个节点(slow.next为第二个链表起点)或2+3个节点(slow为第二个链表起点)都可以

链表长度为6(偶数):
快指针:1 -> 3 -> 5 -> null
慢指针:1 -> 2 -> 3 -> 4
期望分成:3+3个节点(slow为第二个链表起点)

这时发现使用slow为第二个链表的起点可以统一以上两种情况。但是进一步考虑,进入递归的时候我们需要把第一个链表的后半截去掉,在这种情况下时slow.prev.next = null,但是ListNode是不支持prev操作的,只能在遍历快慢指针的时候加上一个prev在找到这个点;但是如果使用前面一种停止条件,直接记录下slow.next再使得slow.next = null即可,两种都是可行的只是前者更加方便。

分开后递归解决两个子问题返回两个sorted list的头,base case为只有一个节点时直接返回。比较两者头的大小决定新的头curr,接下来的过程和merge sort中merge的过程是一致的,不断比较两个链表头元素的大小来选择下一个添加的元素,如果一个链表先被使用完则直接接上另一个链表返回结果。

整个过程中只是修改ListNode中的指针而没有用到new ListNode()的操作,因而有 O ( 1 ) O(1) O(1)的内存使用。

代码

/**
 * 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 fast = head, slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode head2 = slow.next;
        slow.next = null;
        ListNode l1 = sortList(head);
        ListNode l2 = sortList(head2);
        ListNode curr;
        if (l1.val < l2.val) {
            curr = l1;
            l1 = l1.next;
        } else {
            curr = l2;
            l2 = l2.next;
        }
        ListNode res = curr;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                curr.next = l1;
                l1 = l1.next;
            } else {
                curr.next = l2;
                l2 = l2.next;
            }
            curr = curr.next;
        }
        if (l1 == null) {
            curr.next = l2;
        } else {
            curr.next = l1;
        }
        
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值