LeetCode第 148 题:排序链表(C++)

148. 排序链表 - 力扣(LeetCode)
在这里插入图片描述

时间复杂度要求O(nlogn),容易让人想到归并或者快排,如果把数据导入到数组里,进行排序很容易实现,但是空间复杂度是O(n),不符合要求。

归并:

换个写法更加易读:

class Solution {
public:
    ListNode* merge(ListNode *left, ListNode *right){
        auto head = new ListNode;
        auto h = head;
        while(left && right){
            if(left->val < right->val){
                h->next = left;
                left = left->next;
            }else{
                h->next = right;
                right = right->next;
            }
            h = h->next;
        }
        h->next = left == NULL ? right : left;
        return head->next;
    }
    ListNode* merge_sort(ListNode *head){
        if(!head->next) return head;
        ListNode *slow = head, *fast = head->next;
        while(fast && fast->next){
            slow = slow->next;
            fast = fast->next->next;
        }
        auto r_head = slow->next;//slow->next右边部分的头结点
        slow->next = NULL;//左边切断开
        auto left = merge_sort(head);
        auto right = merge_sort(r_head);
        return merge(left, right);
    }
    ListNode* sortList(ListNode* head) {
        if (!head || !head->next) return head;
        return merge_sort(head);
    }
};

不过这个代码的空间复杂度不知道算是O(1)还是O(logn),因为不知道递归栈要不要算进去。那递归的代码都可以写成非递归的,那就改成迭代的看看:
参考题解:

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if (!head || !head->next) return head;
        int n = 0;
        for(auto i = head; i != NULL; i = i->next)  ++n; //n为链表长度
        auto dummy = new ListNode(-1); 
        dummy->next = head;
        // 总体循环次数为logn
        for(int i = 1; i < n; i += i){
            auto beg = dummy;
            // 最开始1个和1个合并为两个,然后2,2->4, 4,4->8
            for(int j = 0; j + i < n; j += 2*i){ //j += 2*i为下一块的起点
                auto left = beg->next;
                auto right = left; 
                for(int k = 0; k < i; ++k)  right = right->next;//right指向第二块的起点,每块有i个节点,越过i个节点即可
                // merge第一块和第二块,起点分别为left, right
                // 第一块的节点数为i, 第二块的节点数可能小于i(为i-1),因为节点个数有奇偶之分,所以需要检查right != NULL 
                int p = 0, q = 0;//计数
                while(p < i && q < i && right != NULL){
                    if(left->val <= right->val){
                        beg->next = left;
                        left = left->next;
                        ++p;
                    }else{
                        beg->next = right;
                        right = right->next;
                        ++q;
                    }
                        beg = beg->next;
                }
                while(p < i){// 可能还有剩余未处理的
                    beg->next = left;
                    beg = beg->next;
                    left = left->next;
                    ++p;
                }
                while(q < i && right != NULL){
                    beg->next = right;
                    beg = beg->next;
                    right = right->next; //right会指向下一块的起点
                    ++q;
                }
                // 处理完之后beg指向的是两块中(已经排序好)元素最大的那个节点
                beg->next = right; //调整beg位置,将beg和下一块连接起来
            }
        }
        return dummy->next;
    }
};

如果使用快速排序,如果可以交换链表的值的话,就会简单很多,而且空间复杂度为o(1):

class Solution {
public:
    ListNode* partition(ListNode *head, ListNode *tail){
        auto pivot = head->val;
        auto left = head;
        auto cur = head->next;
        while(cur != tail){
            if(cur->val < pivot){
                left = left->next;
                swap(left->val, cur->val);
            }
            cur = cur->next;
        }
        swap(head->val, left->val);
        return left;
    }
    void quickSort(ListNode *head, ListNode *tail){//不包含tail
        if(head == tail || head->next == tail)    return; //head->next == tail表示区间内只有一个节点
        auto q = partition(head, tail);
        quickSort(head, q);
        quickSort(q->next, tail);
    }
    ListNode* sortList(ListNode* head) {
        if (!head || !head->next) return head;
        quickSort(head, NULL);
        return head;
    }
};

如果不能交换链表的值的话,那可以根据pivot的值,将不同的节点接到不同的头(两种头,大于pivot,小于pivot)下面(相当于按照pivot分类),然后递归。最后进行相连。

class Solution {
public:
    ListNode* quicksort(ListNode *head){
        if(!head || !head->next)    return head;
        auto pivot = head->val;
        auto left = new ListNode(-1); //左边部分的开头
        auto right = new ListNode(-1); //右边部分的开头
        ListNode *l = left,*r=  right;
        for(auto cur = head; cur != NULL; cur = cur->next){
            if(cur->val < pivot){
                l->next = cur;
                l = l->next;
            }else{
                r->next = cur; //那么此处第一个就是head
                r = r->next;
            }
        }
        l->next = NULL;
        r->next = NULL;
        // 现在left->next即为左边部分的头结点
        //right->next即为pivot
        //riight->next->next即为右边部分的头结点
        auto p = quicksort(left->next); 
        auto q = quicksort(right->next->next);
        //上面分治的时候切断了, 最后需要再进行连接
        //p可能为NULL
        if(!p)   left->next = right->next;
        else{
            left->next = p; 
            while(p->next != NULL)  p = p->next; //p指向末尾
            p->next = right->next; //right->next即为pivot
        }
        right->next->next = q;
        return left->next;
    }
    ListNode* sortList(ListNode* head) {
        if (!head || !head->next) return head;
        return quicksort(head);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值