leetcode 148. 排序链表  medium (重要)

本文详细解析了LeetCode上第148题“排序链表”的解决方案,介绍了两种归并排序的方法:递归版和自底向上版。通过实例展示了如何在O(n log n)的时间复杂度和常数级空间复杂度下,对链表进行排序。

leetcode 148. 排序链表  medium (重要)         

题目描述:

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

解题思路:

方法1: 递归版归并排序  时间O(n log n)  空间O(logn)

主要就是在sortlist中将链表从中间断开,分成两部分,左右两边再分别调用排序的递归函数 sortList(),得到各自有序的链表后,再进行 merge(),这样整体就是有序的了。

方法2:自底向上归并排序

主要就是记住:

1.最后一段会比较特殊,假如最后一段size小于i,则不用排了(一定要有这个条件)

 所以第二层循环是 for(int j=0;j+i<n;j += 2*i)

2. 因为右半边可能没有i个,所以right提前来到nullptr

所以归并每一段的循环条件是 while(num_left && num_right && right)

3.最后别忘了搞完了一段,要连接下一段

begin->next=right;

递归代码:

//递归归并
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(!head || !head->next)
            return head;
        ListNode* slow=head,*fast=head;
        while(fast->next && fast->next->next){
            slow=slow->next;
            fast=fast->next->next;
        }
        ListNode *right=slow->next;
        slow->next=nullptr;   // 断开这一步很关键
        return merge(sortList(head),sortList(right));
        
    }
    
    ListNode* merge(ListNode* left, ListNode *right){
        
        
        ListNode temp(-1);
        ListNode *cur=&temp;
        while(left && right){
            if(left->val <= right->val){
                cur->next=left;
                cur=cur->next;
                left=left->next;
            } else{
                cur->next=right;
                cur=cur->next;
                right=right->next;
            }
        }
        
        
        if(left)
            cur->next=left;
        if(right)
            cur->next=right;
        
        return temp.next;
        
        
    }
    
    
};

循环代码:

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(!head || !head->next )
            return head;
        
        // 统计节点个数
        int n=0;
        for(ListNode *cur=head;cur;cur=cur->next)
            ++n;
        
        ListNode dummy(-1);
        dummy.next=head;
        //每次排序的size是i
        for(int i=1;i<n;i <<=1){
            auto begin=&dummy;
            //排序每一段,假如最后一段size小于i,则不用排了(一定要有这个条件)
            for(int j=0;j+i<n;j += 2*i){
                ListNode* left=begin->next, *right=left;
                
                //让right指向这一段右半边的起点
                for(int k=0;k<i;++k){
                    right=right->next;
                }
                
                //归并这一段
                int num_left=i,num_right=i;  //左半边待排个数,右半边待排个数
                while(num_left && num_right && right){   //因为右半边可能没有i个,所以right提前来到nullptr
                    if(left->val <=right->val){
                        begin->next=left;
                        begin=begin->next;
                        left=left->next;
                        --num_left;
                    } else {
                        begin->next=right;
                        begin=begin->next;
                        right=right->next;
                        --num_right;
                    }
                }
                
                while(num_left){
                    begin->next=left;
                    begin=begin->next;
                    left=left->next;
                    --num_left;
                }
                
                
                while(num_right && right){
                    begin->next=right;
                    begin=begin->next;
                    right=right->next;
                    --num_right;
                }
                
                //连接下一段! 很重要!!!!
                begin->next=right;
                
            }
        }
        
        return dummy.next;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值