写点什么

算法题学习 --- 单链表的排序

作者:桑榆
  • 2022-11-17
    广东
  • 本文字数:2217 字

    阅读完需:约 7 分钟

题目

描述

给定一个节点数为 n 的无序单链表,对其按升序排序。

数据范围:0<n≤100000

要求:空间复杂度 O(n),时间复杂度 O(nlogn)

示例 1

输入:[1,3,2,4,5]返回值:{1,2,3,4,5}
复制代码

示例 2

输入:[-1,0,-2]返回值:{-2,-1,0}
复制代码

分析

思路一

实际上刨除链表的形式,我们知道,这就是一个排序问题,首先我们能想到的比较简单的就是插入排序方式。

  • 将链表分为已排序的部分(开始时只有一个元素)和后面未排序的部分,遍历未排序的部分元素;

  • 在已排序的部分中从前往后遍历查找合适的位置(元素大小大于当前待排序的元素,或走到了已排序部分的末尾);

  • 将当前待排序元素插入到找出的合适位置,遍历直到末尾,整个链表已排序完毕。

上述方案中需要增加一个表头元素,空间复杂度为 O(1),最差情况下时间复杂度为 O(n^2)。

思路二

对于排序问题,归并排序的时间复杂度可以达到 O(nlogn),我们就来看看如何将链表排序的问题分解成子问题。

首先我们要将链表分为前后两个部分,这里我们可以用到快慢指针,快指针每次走两步,慢指针走一步,这样可以找到链表的中点;然后对这两个部分分别做排序处理,最后将得到的结果使用两个有序链表排序的算法连接成一条链表。

特别地,子问题中,如果头指针为空或头指针的 next 为空,视为已排序,立即返回。

步骤如下:

  • step 1:首先判断链表为空或者只有一个元素,直接就是有序的。

  • step 2:准备三个指针,快指针 right 每次走两步,慢指针 mid 每次走一步,前序指针 left 每次跟在 mid 前一个位置。三个指针遍历链表,当快指针到达链表尾部的时候,慢指针 mid 刚好走了链表的一半,正好是中间位置。

  • step 3:从 left 位置将链表断开,刚好分成两个子问题开始递归。

  • step 4:将子问题得到的链表合并。

代码示例

版本一

使用插入排序,使用一个额外的空间 O(1),时间复杂度 O(n^2)

/** * struct ListNode { *  int val; *  struct ListNode *next; * }; */
class Solution { public: /** * * @param head ListNode类 the head node * @return ListNode类 */ ListNode* sortInList(ListNode* head) { // write code here if (nullptr == head || nullptr == head->next) { return head; }
ListNode* pReturn = new ListNode(-1); pReturn->next = head;
// split the link list ListNode* pCur = head->next; head->next = nullptr; ListNode* pNext = nullptr;
while (nullptr != pCur) { // find the location ListNode* pPreSearch = pReturn; ListNode* pSearch = pReturn->next; while (nullptr != pSearch) { if (pSearch->val > pCur->val) { break; } pPreSearch = pSearch; pSearch = pSearch->next; }
// link the node pNext = pCur->next; pCur->next = pPreSearch->next; pPreSearch->next = pCur; pCur = pNext; } return pReturn->next; }};
复制代码

版本二

使用归并排序的方式,时间复杂度 O(nlogn),空间复杂度 O(n)

  • 终止条件: 当子链表划分到为空或者只剩一个节点时,不再继续划分,往上合并。

  • 返回值: 每次返回两个排好序且合并好的子链表。

  • 本级任务: 找到这个链表的中间节点,从前面断开,分为左右两个子链表,进入子问题排序。

/** * struct ListNode { *  int val; *  struct ListNode *next; * }; */
class Solution { public: /** * * @param head ListNode类 the head node * @return ListNode类 */ ListNode* sortInList(ListNode* head) { // write code here return MergeSort(head); } ListNode* MergeSort(ListNode* head){ if (nullptr == head || nullptr == head->next) { return head; }
ListNode *pQuick = head; ListNode *pSlow = head; ListNode *pPre = nullptr;
while(pQuick && pQuick->next){ pQuick = pQuick->next->next; pPre = pSlow; pSlow = pSlow->next; } pPre->next = nullptr; ListNode* pLeft = MergeSort(head); ListNode* pRight = MergeSort(pSlow); return Merge2List(pLeft, pRight); }
ListNode* Merge2List(ListNode *head1,ListNode* head2){ if(nullptr == head1){ return head2; } if(nullptr == head2){ return head1; }
ListNode *pReturn = new ListNode(-1); ListNode *pCur = pReturn;
while(nullptr != head1 && nullptr != head2){ if(head1->val < head2->val){ pCur->next = head1; head1 = head1->next; }else{ pCur->next = head2; head2 = head2->next; } pCur = pCur->next; }
pCur->next = nullptr == head1 ? head2 : head1; return pReturn->next; }};
复制代码


发布于: 刚刚阅读数: 4
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020-02-29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
算法题学习---单链表的排序_算法题_桑榆_InfoQ写作社区