排序-归并排序

例题:148. 排序链表
网址:https://leetcode-cn.com/problems/sort-list/
前沿:这个题目既是链表排序的题目也是常规排序算法的考察,常规解法应该考虑的是插入排序,但是时间复杂度O(n^2)。如果使得时间复杂度降低到O(nlogn)甚至空间复杂度降至O(1)那么只能采用归并排序,堆排序,快速排序(最坏情况也是n*n)。考虑到这题数据存储考虑的是链表数据采用归并排序。归并排序分为自顶向下和自底向上两种,第一种需要使用栈,空间复杂度O(logn),第二种空间复杂度则是O(1)。

方法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 {
public:
    ListNode* sortList(ListNode* head) {
        //插入排序
        ListNode* Head = new ListNode(0);
        Head->next = nullptr;
        ListNode* current = Head;
        ListNode* p = Head->next;
        while(head!=nullptr){
            p = head;
            head = head->next;
            current = Head;    //每次需要从头开始比较
            while(current->next!=nullptr && current->next->val<p->val){
                current = current->next;
            }
            p->next = current->next;
            current->next = p;
        }
        return Head->next;
    }
};

注意点:时间复杂度过高,LedeCode并过不了,而且需要注意的是:1.每次遍历记得更新当前节点,为了避免闭环,Head必须是一个新的开始节点,避免和前面节点起冲突。

方法2(自顶向下的归并排序):

/**
 * 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 {
public:
    ListNode* sortList(ListNode* head) {
        //归并排序
        return sortL(head, nullptr);
    }

    ListNode* sortL(ListNode* head, ListNode* tail){
        if(head == tail) return head;
        if(head->next == tail){
            head->next = nullptr;
            return head;
        }
        //寻找中间节点
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast!=tail){
            fast = fast->next;
            slow = slow->next;
            if(fast!=tail){
                fast=fast->next;
            }
        }
        ListNode* mid = slow;
        return merge(sortL(head, mid), sortL(mid, tail));
    }

    ListNode* merge(ListNode* head1, ListNode* head2){
        ListNode* Head = new ListNode(0);
        ListNode* tmp = Head;
        Head->next = nullptr;
        while(head1!=nullptr && head2!=nullptr){
            if(head1->val<=head2->val){
                tmp->next = head1;
                head1 = head1->next; 
            }else{
                tmp->next = head2;
                head2 = head2->next;
            }
            tmp = tmp->next;
        }
        if(head1!=nullptr) tmp->next = head1;
        if(head2!=nullptr) tmp->next = head2;
        return Head->next;
    }
};

注意点:1.寻找中间点使用的是快慢指针;2.迭代的时候注意处理节点的中间边界,也就是当子序列长度为2的时候注意保留第一个部分,子序列的最后一个节点总是用来废弃的;3.合并排序的注意对于采用头指针方式更好写。

方法3(自底向上的归并排序)

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if (head == nullptr) {
            return head;
        }
        int n = 0;
        ListNode* p = head;
        while(p!=nullptr){
            n++;
            p = p->next;
        }
        ListNode* Head = new ListNode(0, head);
        for(int sublength=1;sublength<n;sublength<<=1){
            ListNode* curr = Head->next;
            ListNode* pre = Head;    //和插入排序类似每次需要考虑从头开始的递归
            while(curr!=nullptr){
                ListNode* head1 = curr;
                for(int i=1;i<sublength&& curr->next!=nullptr;i++){
                    curr = curr->next;
                }
                ListNode* head2 = curr->next;
                curr->next = nullptr;
                curr = head2;
                for(int i=1;i<sublength&& curr!=nullptr && curr->next!=nullptr;i++){   //这一块的curr!=nullptr,一定要注意
                    curr = curr->next;
                }
                ListNode* next = nullptr;
                if (curr != nullptr) {
                    next = curr->next;
                    curr->next = nullptr;
                }
                pre->next = merge(head1, head2);
                while(pre->next!=nullptr){
                    pre = pre->next;
                }
                curr = next;
            }
        }
        return Head->next;
    }
    ListNode* merge(ListNode* head1, ListNode* head2) {
        ListNode* dummyHead = new ListNode(0);
        ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;
        while (temp1 != nullptr && temp2 != nullptr) {
            if (temp1->val <= temp2->val) {
                temp->next = temp1;
                temp1 = temp1->next;
            } else {
                temp->next = temp2;
                temp2 = temp2->next;
            }
            temp = temp->next;
        }
        if (temp1 != nullptr) {
            temp->next = temp1;
        } else if (temp2 != nullptr) {
            temp->next = temp2;
        }
        return dummyHead->next;
    }
};

注意点:一个是归并排序自底向上的实现通过长度的来实现循环,另一个是需要考虑针对后一个序列的指针迭代要考虑到是否处理指针空指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值