力扣-148-排序链表
题目分析:题目要求时间复杂度为$O(nlogn)$,空间复杂度为$O(1)$,根据时间复杂度,我们自然能想到二分,故这里要用到归并排序。对链表的排序,可以通过修改指针来更改节点顺序,无需像数组一样额外开辟存储空间。归并排序有递归和非递归的做法,这里采用递归的做法。
首先,讲一下什么是归并排序?
归并排序采用了分治法的思想,对于一个很大的数列的排序,将其对等分为左右两个子数列进行排序,按照递归的思想继续对等划分,直至数列中只有一个元素(一个元素就不用排序了)。然后再一层一层地合并(注意这里合并是按照小的再前进行的)。
具体的做法如下:
-
分割$cut$环节: 找到当前链表中点,并从中点将链表断开(以便在下次递归$cut$时,链表片段拥有正确边界);
我们使用$fast$和$slow$快慢双指针法,奇数个节点找到中点,偶数个节点找到中心左边的节点。
找到中点$slow$后,执行$slow->next = NULL$将链表切断。
递归分割时,输入当前链表左端点$head$和中心节点$slow$的下一个节点$temp$(因为链表是从$slow$切断的)。
$cut$递归终止条件: 当$head.next == NULL$时,说明只有一个节点了,直接返回此节点。 -
合并$merge$环节: 将两个排序链表合并,转化为一个排序链表。
双指针法合并,建立辅助$ListNode* h $作为头部。
设置两指针$ left$, $right$ 分别指向两链表头部,比较两指针处节点值大小,由小到大加入合并链表头部,指针交替前进,直至添加完两个链表。
返回辅助$ListNode* h$作为头部的下个节点 $h->next$。
时间复杂度$O(l + r)$,$l$, $r$ 分别代表两个链表长度。 - 注意:如果$head==NULL$或者$head->next==NULL$时直接返回$head$即可。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* sortList(ListNode* head) { if (head == NULL || head->next == NULL) return head; //寻找链表的中点:链表有奇数个节点,slow返回List的中点;链表有偶数个节点,slow返回List中心的左侧 ListNode* fast = head->next; ListNode* slow = head; while(fast != NULL && fast->next != NULL) { slow = slow->next; fast = fast->next->next;//快指针指到链表末尾时,慢指针刚好指到链表中间 } ListNode* temp = slow->next; slow->next = NULL; //将左链表和右链表分别排序 ListNode* left = sortList(head); ListNode* right = sortList(temp); ListNode* h = new ListNode(0); ListNode* result = h; //合并 while(left != NULL && right != NULL) { if (left->val < right->val) { h->next = left; left = left->next; } else { h->next = right; right = right->next; } h = h->next; } h->next = left == NULL? right:left; return result->next; } };
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!