链表排序-观“归并排序”有感

先贴上LeetCode原题链接:
链表排序
注意:原题要求是时间复杂度O(nlogn),空间复杂度O(1)
两种解法时间复杂度都是O(nlogn),递归的空间复杂度为O(n),迭代的空间复杂度为O(1)

解法一:归并排序(递归)

关于递归的通用思路:

  1. 先找终止条件,如该题中终止条件head == NULL || head->next ==NULL
  2. 思考返回值,每一级递归向上要返回什么信息
  3. 单步怎么写,也是最关键的步骤,在该题中也就是如何将两个排好序的链表合并。

通过递归实现链表归并排序,有以下两个环节:

  • 分割cut环节:找到当前链表中点,并从中点将链表断开

    • 因为链表无法使用索引来随机访问,所以我们使用slow,fast快慢指针法来找到中间节点,找到中点slow后,执行slow->next = NULL将链表切断。
    • cut递归的终止条件:head->next == NULL,说明只有一个节点了,直接返回节点。
  • 合并merge环节:将两个排序链表合并,重要性不再强调,要能够熟练掌握。

template<typename T>
struct ListNode
{
    T value;
    ListNode<T>* next ;

    ListNode(T val) : value(val),next(NULL){}
};



template<typename T>
ListNode<T>* listsort(ListNode<T> *head){
    if(head == NULL || head->next == NULL)
        return head;
    ListNode<T> *slow = head;
    ListNode<T> *fast = head->next;
    while(fast != NULL && fast->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
    }
    ListNode<T> *tmp = slow->next;
    slow->next = NULL;

    ListNode<T> *left = listsort(head);
    ListNode<T> *right = listsort(tmp);
    
    //合并两个有序链表
    ListNode<T>* h = new ListNode<T>(-1);
    ListNode<T> *cur = h;
    while (left != NULL && right != NULL)
    {
        if(left->value < right->value)
        {
            cur->next = left;
            left = left->next;   
        }
        else
        {
            cur->next = right;
            right = right->next;
        }
        cur = cur->next;
    }
    cur->next = left != NULL ? left : right;

    return h->next;
}

解法二:归并排序(迭代)—自底而上

合并的过程上面已经讲过,这里的关键在于链表的断链,再挂接

  • 分割cut环节:分割的size从1迭代到链表length,直接上示意图
    在这里插入图片描述
 ListNode* listsort(ListNode* head)
    {
        ListNode dummyHead(0);
        dummyHead.next = head;
        auto p = head;
        int length = 0;
        while(p)
        {
            ++length;
            p=p->next;
        }

        cout<<"length = "<<length<<endl;

        for(int size = 1;size < length;size <<=1)
        {
            auto cur = dummyHead.next;
            auto tail = &dummyHead;
            while (cur)
            {
                //断链
                auto left = cur;
                auto right = cut(cur ,size);
                cur = cut(right,size);//更新下一次while循环的头指针

                //双路归并,输出归并后的头、尾指针
                ListNode* mergedHead = NULL;
                ListNode* mergedTail = NULL;
                merge(left,right,mergedHead,mergedTail);

                //挂接
                tail->next = mergedHead;
                tail = mergedTail;//更新下一次while循环的尾指针

            }
        }
        return dummyHead.next;
    }

    ListNode* cut(ListNode* head, int size)
    {
        ListNode* cur = head;
        while(--size && cur)
        {
            cur = cur->next;
        }

        if(!cur) return NULL;

        auto next = cur->next;
        cur->next = NULL;
        return next;
    }

    void merge(ListNode* left, ListNode* right, ListNode*& head, ListNode*& tail)
    {
        ListNode dummyHead(0);
        auto p = &dummyHead;
        
        while(left&&right)
        {
            if(left->value < right->value)
            {
                p->next = left;
                left = left->next;
            }else
            {
                p->next = right;
                right = right->next;
            }
            p = p->next;
        }
        p->next = left ? left : right;
        while(p->next)
        {
            p = p->next;
        }
        head = dummyHead.next;
        tail = p;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值