Sort a linked list in
O(
n log
n) time using constant space complexity. 对一个链表进行排序,且时间复杂度要求为 O(n log n) ,空间复杂度为常量。一看到 O(n log n) 的排序,首先应该想到归并排序和快速排序,但是通常我们使用这两种排序方法时都是针对数组的,现在是链表了。 归并排序法:在动手之前一直觉得空间复杂度为常量不太可能,因为原来使用归并时,都是 O(N)的,需要复制出相等的空间来进行赋值归并。对于链表,实际上是可以实现常数空间占用的(链表的归并排序不需要额外的空间)。利用归并的思想,递归地将当前链表分为两段,然后merge,分两段的方法是使用 fast-slow 法,用两个指针,一个每次走两步,一个走一步,知道快的走到了末尾,然后慢的所在位置就是中间位置,这样就分成了两段。merge时,把两段头部节点值比较,用一个 p 指向较小的,且记录第一个节点,然后 两段的头一步一步向后走,p也一直向后走,总是指向较小节点,直至其中一个头为NULL,处理剩下的元素。最后返回记录的头即可。 主要考察3个知识点,
知识点1:归并排序的整体思想
知识点2:找到一个链表的中间节点的方法
知识点3:合并两个已排好序的链表为一个新的有序链表
归并排序的基本思想是:找到链表的middle节点,然后递归对前半部分和后半部分分别进行归并排序,最后对两个以排好序的链表进行Merge。
/** * 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; return mergeSort(head); } ListNode* mergeSort(ListNode*head) { if(head->next==NULL) return head; ListNode* pHead,*qHead,*pre; pHead=head; qHead=head; pre=NULL; while(qHead!=NULL&&qHead->next!=NULL) { qHead=qHead->next->next; pre=pHead; pHead=pHead->next; } pre->next=NULL; ListNode *l,*r; l=mergeSort(head); r=mergeSort(pHead); return merge(l,r); } ListNode* merge(ListNode *l,ListNode*r) { ListNode *pRes=new ListNode(0); ListNode *temp=pRes; while(l!=NULL&&r!=NULL) { if(l->val<=r->val) { temp->next=l; temp=temp->next; l=l->next; } else { temp->next=r; temp=temp->next; r=r->next; } } if(l!=NULL) temp->next=l; if(r!=NULL) temp->next=r; temp=pRes->next; delete pRes; return temp; } };