题目:Sort a linked list in O(n log n)
time using constant space complexity.
思路:题目要求我们使用常数空间复杂度,时间复杂度为O(nlog(n)). 满足这个时间复杂度的有快速排序,归并排序,堆排序。插入排序时间复杂度为O(n^2). 双向链表用快排比较合适,堆排序也可用于链表,单项链表适合于归并排序。我们就用归并排序的思想来完成链表的排序。
首先是用快慢双指针找到链表中间的位置,然后分成前后端分别递归的归并排序,最后合并。
Attention:
1. 快慢指针找中间节点的方法,是常用方法,要熟练使用。
//将链表分成前后两部分 双指针找到中间节点 ListNode* fast = head; ListNode* slow = head; while(fast->next != NULL && fast->next->next != NULL) { slow = slow->next; fast = fast->next->next; }
2. 链表的数据结构是如何实现归并的方法的。定义一个合并后的链表,然后把合适的节点放进去。思想和数组是一样的,只是链表操作不一样。
复杂度:如果这里我们考虑递归的栈空间的话,空间复杂度是O(lg(n))
AC Code:
/** * 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; //将链表分成前后两部分 双指针找到中间节点 ListNode* fast = head; ListNode* slow = head; while(fast->next != NULL && fast->next->next != NULL) { slow = slow->next; fast = fast->next->next; } ListNode* head2 = slow->next; slow->next = NULL; ListNode* head1 = head; head1 = sortList(head1); //前半段归并排序 head2 = sortList(head2); //后半段归并排序 return merge(head1, head2); } private: ListNode* merge(ListNode* h1, ListNode* h2) { ListNode* dummyhead = new ListNode(0); ListNode* mlist = dummyhead; while(h1 != NULL && h2 != NULL) { if(h1->val <= h2->val) { mlist->next = h1; h1 = h1->next; } else { mlist->next = h2; h2 = h2->next; } mlist = mlist->next; } if(h1 != NULL) { mlist->next = h1; } if(h2 != NULL) { mlist->next = h2; } return dummyhead->next; } };
我们也可以不使用递归的方法,自底向上的非递归版本的归并排序,空间复杂度就是常数了。可以看下这篇博文:sort list.
排序是面试中很常见和基础的一个主题,我们需要对各种常见的排序算法要熟悉。尤其是算法的原理,很多题目虽然没有直接考察排序的实现,但是会用到其中的思想,比如经典的topK问题,用到了快排的原理。这篇文章中有提到,可以看看:Median of Two Sorted Arrays.所以我们应该要更加注重排序算法的原理。
时间: 2024-11-08 19:21:49