Leetcode每日一题:148.sort-list(链表排序)

在这里插入图片描述
思路:对给定的链表进行排序,我最先想到的就是用昨天做的插入排序,一提交发现超时了,一看它这里要求时间复杂度为O(n logn)并且空间复杂度为常数级;链表的插入排序无疑复杂度为O(n2);后来使用归并排序,复杂度才达到要求;
这是其中的一篇题解,我觉得写的挺详细的:链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因为递归使用栈,所以消耗了很大的内存;

// 找到链表中间节点(876. 链表的中间结点)
ListNode *middleNode(ListNode *head)
{
    if (!head || !head->next)
        return head;
    ListNode *slow = head;
    ListNode *fast = head->next->next;
    while (fast != nullptr && fast->next != nullptr)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

// 合并两个有序链表(21. 合并两个有序链表)
ListNode *merge(ListNode *h1, ListNode *h2)
{
    ListNode *h = new ListNode(-1), *head = h;
    while (h1 && h2)
    {
        if (h1->val < h2->val)
        {
            h->next = h1;
            h1 = h1->next;
        }
        else
        {
            h->next = h2;
            h2 = h2->next;
        }
        h = h->next;
    }
    while (h1)
    {
        h->next = h1;
        h = h->next;
        h1 = h1->next;
    }
    while (h2)
    {
        h->next = h2;
        h = h->next;
        h2 = h2->next;
    }
    h->next = nullptr;
    return head->next;
}

ListNode *sortList(ListNode *head)
{
    // 递归结束条件
    if (head == NULL||head->next == NULL)
        return head;
    // 找到链表中间节点并断开链表
    ListNode *midNode = middleNode(head);
    ListNode *rightHead = midNode->next;
    midNode->next = nullptr;

    // 递归
    ListNode *left = sortList(head);
    ListNode *right = sortList(rightHead);

    //合并有序链表
    return merge(left, right);
}

非递归版就是一个自底向上的过程:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

性能提升很快:
在这里插入图片描述

    ListNode *sortList(ListNode *head)
    {
        ListNode *h = head;
        int len = 0;
        int intv = 1; //每次处理的节点数

        //得到链表长度
        while (h)
        {
            h = h->next;
            len++;
        }

        ListNode *res = new ListNode(0);
        res->next = head;

        while (intv < len)
        {
            ListNode *pre = res;
            ListNode *h = res->next;
            ListNode *h1, *h2; //需要合并的两个子链表头节点
            int i;
            while (h)
            {
                h1 = h;
                i = intv;
                while (i && h)
                {
                    h = h->next;
                    i = i - 1;
                }
                // 如果i>0 说明不需要归并,因为h2为空,即intv比链表总个数还多
                if (i)
                    break;
                // 否则,进行归并
                h2 = h;
                i = intv;
                while (i && h)
                {
                    h = h->next;
                    i = i - 1;
                }

                int c1 = intv;
                int c2 = intv - i;

                // 合并
                while (c1 && c2)
                {
                    if (h1->val < h2->val)
                    {
                        pre->next = h1;
                        h1 = h1->next;
                        c1 = c1 - 1;
                    }
                    else
                    {
                        pre->next = h2;
                        h2 = h2->next;
                        c2 = c2 - 1;
                    }
                    pre = pre->next;
                }

                pre->next = c1 == 0 ? h2 : h1;

                while (c1 > 0 || c2 > 0)
                {
                    pre = pre->next;
                    c1--;
                    c2--;
                }
                pre->next = h;
            }

            intv = intv << 1;
        }
        return res->next;
    }
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页