力扣 #148排序链表 笔记(归并排序)

3 篇文章 0 订阅

题目:

在这里插入图片描述
因为时间复杂度要求(n*logn),所以考虑归并排序(但归并排序并不满足常数级的空间复杂度,还需要使用快慢指针?)。

参考链接:https://www.cnblogs.com/chengxiao/p/6194356.html

归并排序:
把一个待排序列从中间分两半,每半再各自应用归并排序处理。分出来的左右两半的归并操作中,同样也是先把每半序列再从中间劈开,分成两个子序列,再应用归并排序,以此类推,由此看是一个递归操作。
因此由此看来是,在每次归并函数调用中,先检测传进来的序列长度,如果序列中只有1个元素,则排不了序直接return返回。如果序列中的元素多于1个,则先把它从中间劈开,分出来的左右两个序列先各自调用归并排序,然后各自的归并排序完成之后,再把左右两个序列的所有元素合一起,来一个排序,这样排完,一次完整的归并函数的主要操作就完成了。

快慢指针参考:https://zhuanlan.zhihu.com/p/38521018
当我们直接在链表原地进行排序的时候,空间复杂的即为常数级。

用序列0、3、5、2、7、1、9、8测试
debug的时候,发现5、2这组从MergeSort()回传的时候,2给丢了,只有5。
原因是这句话:

MergeSort(A);
MergeSort(slow);
return Merge(A, slow);

如果里面的两个MergeSort()调用单独拿出来写成两句话的话,必须要有两个变量接着他们返回来的值,否则A和slow依然指着自己在调用MergeSort()前的两个原结构体,即是链表被排序了他俩也还指着原来两个结构体,之后再调用Merge(A, slow)的话这个调用逻辑就出错了。
所以要么写成这样:

A = MergeSort(A);
slow = MergeSort(slow);
return Merge(A, slow);

要么写成这样:

return Merge(MergeSort(A), MergeSort(slow));

源码:

#include <iostream>


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) {}
};


void dis_ListNode(ListNode *head)
{
    for(ListNode *p = head; p; p = p->next)
    {
        std::cout << p->val << " ";
    }
    std::cout << std::endl;
}


class Solution
{
public:
    ListNode* sortList(ListNode *A);
    ListNode* Merge(ListNode *Left, ListNode *Right);
};

ListNode* Solution::sortList(ListNode *A)
{
    if(!A || !(A->next))
    {
        return A;
    }

    ListNode *fast, *slow, *pre;   // 先用快慢指针找到链表A的中间节点,把A劈两半,pre在while结束后最终指向左半部分的最后一个节点
    pre = fast = slow = A;
    while(fast && fast->next)
    {
        pre = slow;
        slow = slow->next;   // slow走一步
        fast = fast->next->next;   // fast走两步,这样两个指针遍历完slow刚好指向中间位置
    }
    pre->next = NULL;

    return Merge(sortList(A), sortList(slow));
}

ListNode* Solution::Merge(ListNode *Left, ListNode *Right)
{
    ListNode *head = nullptr;

    if(Left->val <  Right->val)    // 确定head节点
    {
        head = Left;
        Left = Left->next;
        head->next = nullptr;
    }
    else
    {
        head = Right;
        Right = Right->next;
        head->next = nullptr;
    }

    ListNode *p = head;   // 开始排序,Left和Right谁小就往后接
    while(Left && Right)
    {
        if (Left->val < Right->val)
        {
            p->next = Left;
            Left = Left->next;
            p = p->next;
        }
        else
        {
            p->next = Right;
            Right = Right->next;
            p = p->next;
        }
    }

    while(Left)
    {
        p->next = Left;
        Left = Left->next;
        p = p->next;
    }
    while(Right)
    {
        p->next = Right;
        Right = Right->next;
        p = p->next;
    }

    p->next = nullptr;

    dis_ListNode(head);

    return head;
}


int main()
{
    int i = 3;   // 创建待排链表
    int val[] = {4, 2, 1, 3};
    ListNode *p = new ListNode(val[i--], nullptr);
    for ( ; i >= 0; --i)
    {
        ListNode *q = new ListNode(val[i], p);
        p = q;
    }
    ListNode *head = p;

    std::cout << "待排链表中的元素:";
    dis_ListNode(head);

    // 归并排序
    Solution *solution = new Solution;
    head = solution->sortList(head);

    std::cout << "排序后的序列:";
    dis_ListNode(head);

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值