编程训练第七十五期——排序链表

编程问题:

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


示例:

  • 输入:head = [4,2,1,3]
    输出:[1,2,3,4]
  • 输入:head = [-1,5,3,4,0]
    输出:[-1,0,3,4,5]
  • 输入:head = []
    输出:[]

解法:

1.线性表+排序(快排)
时间复杂度O(NlogN)
空间复杂度O(N)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
private:
    static bool compare(ListNode *a, ListNode *b)
    {
        return a->val < b->val;
    }
    
public:
    ListNode* sortList(ListNode* head) {
        if (!head) return head;
        vector<ListNode *> temp;
        ListNode *cur = head;
        ListNode *h = new ListNode(0);
        int i;
        while (cur)
        {
            temp.push_back(cur);
            cur = cur->next;
        }
        sort(temp.begin(), temp.end(), compare);
        h->next = temp[0];
        for (i = 0; i < temp.size() - 1; i++)
        {
            temp[i]->next = temp[i + 1];
        }
        temp[i]->next = nullptr;
        return h->next;
    }
};

2.归并排序+递归
对链表自顶向下归并排序的过程如下。
找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。对两个子链表分别排序。将两个排序后的子链表合并,得到完整的排序后的链表。将两个有序的子链表进行合并。上述过程可以通过递归实现。递归的终止条件是链表的节点个数小于或等于 1,即当链表为空或者链表只包含 1 个节点时,不需要对链表进行拆分和排序。

时间复杂度O(NlogN)
空间复杂度O(logN)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
private:
    ListNode *sortNode(ListNode *front, ListNode *rear)
    {
        if (!front)
            return front;
        if (front->next == rear)
        {
            front->next = nullptr;
            return front;
        }
        ListNode *slow = front;
        ListNode *fast = front;
        while (fast != rear)
        {
            slow = slow->next;
            fast = fast->next;
            if (fast != rear)
                fast = fast->next;
        }
        ListNode *mid = slow;
        return merge(sortNode(front, mid), sortNode(mid, rear));
    }

    ListNode *merge(ListNode *a, ListNode *b)
    {
        ListNode *dummyHead = new ListNode(0);
        ListNode *temp = dummyHead;
        ListNode *tempA = a;
        ListNode *tempB = b;
        while (tempA && tempB)
        {
            if (tempA->val < tempB->val)
            {
                temp->next = tempA;
                tempA = tempA->next;
            }
            else
            {
                temp->next = tempB;
                tempB = tempB->next;
            }
            temp = temp->next;
        }
        if (tempA)
            temp->next = tempA;
        else if (tempB)
            temp->next = tempB;
        return dummyHead->next;
    }
    
public:
    ListNode* sortList(ListNode* head) {
        return sortNode(head, nullptr);
    }
};

3.归并排序
使用自底向上的方法实现归并排序,则可以达到 O(1) 的空间复杂度。
首先求得链表的长度 length,然后将链表拆分成子链表进行合并。用 subLength 表示每次需要排序的子链表的长度,初始时subLength=1。每次将链表拆分成若干个长度为 subLength 的子链表(最后一个子链表的长度可以小于 subLength),按照每两个子链表一组进行合并,合并后即可得到若干个长度为 subLength×2 的有序子链表(最后一个子链表的长度可以小于 subLength×2)。将 subLength 的值加倍,重复操作,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 length,整个链表排序完毕。

时间复杂度O(NlogN)
空间复杂度O(1)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
private:
    ListNode *merge(ListNode *a, ListNode *b)
    {
        ListNode *dummyHead = new ListNode(0);
        ListNode *temp = dummyHead;
        ListNode *tempA = a;
        ListNode *tempB = b;
        while (tempA && tempB)
        {
            if (tempA->val < tempB->val)
            {
                temp->next = tempA;
                tempA = tempA->next;
            }
            else
            {
                temp->next = tempB;
                tempB = tempB->next;
            }
            temp = temp->next;
        }
        if (tempA)
            temp->next = tempA;
        else if (tempB)
            temp->next = tempB;
        return dummyHead->next;
    }
    
public:
    ListNode* sortList(ListNode* head) {
        ListNode *searchLength = head;
        int length = 0;
        while (searchLength)
        {
            length++;
            searchLength = searchLength->next;
        }
        ListNode *dummyHead = new ListNode(0);
        dummyHead->next = head;
        for (int sublength = 1; sublength < length; sublength *= 2)
        {
            ListNode *pre = dummyHead, *cur = pre->next;
            while (cur)
            {
                ListNode *head1 = cur;
                for (int i = 1; i < sublength && cur->next; i++)
                {
                    cur = cur->next;
                }
                ListNode *head2 = cur->next;
                cur->next = nullptr;
                cur = head2;
                for (int i = 1; i < sublength && cur && cur->next; i++)
                {
                    cur = cur->next;
                }
                ListNode *temp = nullptr;
                if (cur)
                {
                    temp = cur->next;
                    cur->next = nullptr;
                }
                ListNode *now = merge(head1, head2);
                pre->next = now;
                while (pre->next)
                {
                    pre = pre->next;
                }
                cur = temp;
            }
        }
        return dummyHead->next;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值