针对链表自底向上的递归排序

来源:3-4 自底向上的归并排序算法

慕粉1548201684

2019-11-22

老师您好,这是我针对leetcode148问题(题目要求在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序)的一个解答。我看了很多人的解答都用到了递归,如果递归的话那空间复杂度应该不满足要求才对,所以我使用了课上所讲的自底向上的归并排序来实现,可是感觉第二层循环的控制不是非常美观,也不太知道该怎么去优化。老师是否能给出这个问题的一个模板解法供我们参考。

class Solution148 {
    public static class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
        }
    }

    public ListNode sortList(ListNode head) {
        //得到链表长度
        int n = 0;
        ListNode dup = head;
        while (dup != null) {
            n++;
            dup = dup.next;
        }

        //dh:用于返回的虚拟头节点;cur:用于遍历当前需要归并的链表;newHead:下一个需要归并的链表头;
        //lh、rh:用于合并的左右两个链表
        ListNode dh = new ListNode(-1);
        dh.next = head;
        ListNode cur;
        for (int sz = 1; sz < n; sz += sz) {
            ListNode pre = dh;
            ListNode newHead = dh.next;
            while (true) {
                cur = newHead;
                ListNode lh = cur;
                for (int i = 0; i < sz - 1 && cur != null; i++) cur = cur.next;
                if (cur == null || cur.next == null) {
                    pre.next = lh;
                    break;
                }
                ListNode rh = cur.next;
                cur.next = null;
                cur = rh;
                for (int i = 0; i < sz - 1 && cur != null; i++) cur = cur.next;
                if (cur == null) {
                    pre.next = mergeTwoList(lh, rh);
                    break;
                }
                newHead = cur.next;
                cur.next = null;
                pre.next = mergeTwoList(lh, rh);
                while (pre.next != null) {
                    pre = pre.next;
                }
            }
        }
        return dh.next;
    }

    //合并两个链表
    private ListNode mergeTwoList(ListNode l1, ListNode l2) {
        ListNode dh = new ListNode(-1);
        ListNode prev = dh;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
        prev.next = l1 == null ? l2 : l1;
        return dh.next;
    }
}
写回答

1回答

liuyubobobo

2019-11-22

赞!


是的,使用递归,空间复杂度,严格来说,应该是 logn 的。不过有的时候,在面试过程中,面试官(或者笔试中的题目)会明确表示忽视系统栈的空间分析:)


对链表进行归并排序,确实自顶向下的递归更好写一些。对于自底向上,我也实现过一个版本,仅供参考(C++):https://github.com/liuyubobobo/Play-Leetcode/blob/master/0148-Sort-List/cpp-0148/main2.cpp


从代码行数上看,似乎你的代码更简洁:)


继续加油!:)

1
1
慕粉1548201684
好的,多谢老师
2019-11-22
共1条回复

算法与数据结构(C++版) 面试/评级的算法复习技能包

课程专为:短时间内应对面试、升职测评等艰巨任务打造

11187 学习 · 1614 问题

查看课程