在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
看到这个O(n*logn)的时间复杂度,常数级的空间复杂度,我第一反应是堆排序,但在链表进行堆排序要访问子树节点时,非常不好处理(结构体也不支持),我只能考虑快速和归并;
- 归并排序:因为顺序表归并排序的空间复杂度是O(n),我总感觉有点不太可能符合要求,但仔细一想,顺序表归并排序时间复杂度那么高的原因是它在进行排序数据段时要创建新的空间用于存储排序后的结果然后存入原数据段,但这是由于顺序表的特性决定的,链表只需改变节点间的指向关系就行了,所以还是很有希望的!
- 大致思路分为三步:
- 1.确立并归思想:将整个链表从中分为两半,若这两部分有序只需进行有序数组合并就行,而这要使这两个子链表有序,它们又要各自再分…直到只剩一个节点时,此时这个子链表必定有序;找到链表中间节点,对两边在进行并归排序,到子链表只剩一个节点时(必为有序)开始向上进行链表合并;也可以理解为二叉树前序遍历
- 2.如何找到中间节点:使用快慢指针,快指针一次走两步,慢指针一次走一步,当快指针到达链表末尾时,慢指针指向中间节点;此处注意快指针每走一步就要判空一次,以免出现解引用NULL;
- 3.有序链表合并(Merge):用两个指针分别便利两个链表,谁小谁往新创立的链表后面进行尾插,然后它的值针往后走一步,当两个链表有一个为空时,将另一个非空链表续在新链表后面;(能传址就传址,能用指针就不创节点,男人就要快!w(゚Д゚)w)
参考代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* Merge(struct ListNode* left,struct ListNode*right){//这就是两个有序链表的合并
struct ListNode* new =NULL;
struct ListNode* back = NULL;
while(left&&right){
if(left->val<=right->val){
if(new==NULL){
new = left;
back = new;
}else{
new->next = left;
new = new->next;
}
left = left->next;
}
else{
if(new==NULL){
new = right;
back = new;
}else{
new->next = right;
new = new->next;
}
right = right->next;
}
}
if(left){
new->next = left;
}
else{
new->next = right;
}
return back;
}
struct ListNode* MergeList(struct ListNode*start){
if(!start->next){return start;}
struct ListNode* mid = start,*right = start,*pre = NULL;
while(mid&&right){//
right = right->next;
if(right){
pre = mid;
mid = mid->next;
right = right->next;
}
}
pre->next = NULL;//将mid前的节点next置空,这样就可以停下;
struct ListNode*l = MergeList(start);
struct ListNode*r = MergeList(mid);//二叉前序遍历;
return Merge(l,r);
}
struct ListNode* sortList(struct ListNode* head) {//题的要求不就是让我用堆排序么?,原来不是,要用并归排序=—=;
if((!head)||(!head->next)){
return head;
}
return MergeList(head);
}