这个题目要求在时间复杂度O(nLogn)、空间复杂度O(1)内,对单链表进行排序。常见的满足这两项要求的排序算法就是归并排序和快排。我先尝试了快排,由于在单链表中实现随机选择哨兵项A[r]不够方便,我令每组最后一个数据项为A[r](参考算法导论实现),这样做对于一般的数据应该没有问题,但对于特殊设计的数据(如本题的输入数据),还是会把快排降为O(N^2)。关于快排单链表的实现见这篇Blog利用快速排序对单链表进行排序。
于是我又尝试了归并排序,这回很顺利就AC了。对链表进行归并,我习惯于分别将他们加上头指针,再进行merge合并。在合并操作时要务必小心,不要把链表指乱,事实证明我写过几次链表合并,每次写的时候还是要想一想,估计写十次以上才能默出吧。对于分裂链表(即寻找中间结点),有个小技巧:用两个指针,都从头部开始,分别以步长1,步长2对链表进行遍历,当第二个指针到达链表尾部,第一个指针就是链表中间。这里我为了令指针1的next节点是我想要的中间位置,所以初始化令指针2指向指针1的Next。最后令mid=指针1->next,指针1->next=NULL来实现对链表的割裂。
下面是代码,main函数和ListNode数据结构与Blog 利用快速排序对单链表进行排序共享,这里就省略了。
class Solution { public: ListNode *sortList(ListNode *head) { if (head == NULL) return head; return mergeSort(head); } ListNode* merge(ListNode* l1, ListNode* l2) { ListNode* head1 = new ListNode(0); head1->next = l1; ListNode* node1 = head1; ListNode* head2 = new ListNode(0); head2->next = l2; ListNode* node2 = head2; while (node1->next != NULL&&node2->next != NULL) { if (node1->next->val <= node2->next->val) { node1 = node1->next; } else{ ListNode* tmp = node1->next; node1->next = node2->next; node2->next = node2->next->next; node1->next->next = tmp; } } if (node2->next != NULL) { node1->next = node2->next; } return head1->next; } ListNode* mergeSort(ListNode* list) { ListNode* p1 = list; ListNode* p2 = list->next; if (p2 == NULL) return list; if (p2->next == NULL) { if (p1->val > p2->val) swap(p1->val, p2->val); return p1; } while (p1->next != NULL&&p2->next != NULL) { p1 = p1->next; p2 = p2->next; if (p2 != NULL) p2 = p2->next; if (p2 == NULL) break; } ListNode* mid = p1; ListNode* head1 = list; ListNode* head2 = p1->next; p1->next = NULL; head1 = mergeSort(head1); head2 = mergeSort(head2); ListNode*res=merge(head1, head2); return res; } };