题目:
Sort a linked list in O(n log n) time using constant space complexity.
题目解析:要求用链表作为排序的数据结构,时间复杂度为O(nlogn),空间复杂度为O(1)。
分析:首先想到复杂度为O(nlogn)的排序算法有:快排,堆排和归并排序,三者的空间复杂度都为O(n)。本例使用归并排序,归并排序如果用数组结构存储归并排序后的数据,则需要开辟出O(n)的空间来容纳数组,空间复杂度为O(n),但是若用链表作为数据结构,只需要改变next指针指向下一个归并后的顺序就行,空间复杂度降为O(1)。
要用归并排序,难点是:怎么样找到分治时的middle指针,采用快慢指针的思想。快指针一次走两步,慢指针一次走一步,当快指针走到头时,慢指针刚好走到中间位置,此位置即为middle的位置。
两个有序序列的合并
1 LinkList* mergelist(LinkList *head1,LinkList *head2) 2 { 3 LinkList *newhead = new LinkList(-1); 4 LinkList *newtail; 5 newtail = newhead; 6 while (head1 != NULL &head2 != NULL) 7 { 8 if (head1->data < head2->data) 9 { 10 newtail->next = head1; 11 head1 = head1->next; 12 }else 13 { 14 newtail->next = head2; 15 head2 = head2->next; 16 } 17 newtail = newtail->next; 18 } 19 if (head1 != NULL) 20 { 21 newtail->next= head1; 22 } 23 if (head2 != NULL) 24 { 25 newtail->next = head2; 26 } 27 return newhead->next; 28 }
得到middle指针
1 LinkList* getmid(LinkList *head) 2 { 3 LinkList *fast,*slow; 4 fast = head; 5 slow = head; 6 while (fast->next != NULL && fast->next->next != NULL) 7 { 8 fast = fast->next->next; 9 slow = slow->next; 10 } 11 fast = slow->next; 12 //从中间节点断开 13 slow->next = NULL; 14 return fast; 15 }
归并函数
1 LinkList* sortlist(LinkList *head) 2 { 3 if (head == NULL || head->next == NULL) 4 { 5 return head; 6 } 7 LinkList *mid; 8 mid = getmid(head); 9 head = sortlist(head); 10 mid = sortlist(mid); 11 head = mergelist(head,mid); 12 return head; 13 }
测试例子补全:
创建单链表:(返回是是没有头结点的链表)
1 LinkList* createlinklist(LinkList *l,int n) 2 { 3 LinkList *p,*s; 4 l = (LinkList *)malloc(sizeof(LinkList)); 5 s = l; 6 int m; 7 for (int i = 0; i < n;i++) 8 { 9 p = (LinkList *)malloc(sizeof(LinkList)); 10 cin >> m; 11 p->data = m; 12 s->next = p; 13 s = p; 14 } 15 s->next = NULL; 16 return l->next; 17 }
主函数:
1 void main() 2 { 3 LinkList *l = new LinkList(-1); 4 l = createlinklist(l,6); 5 l = sortlist(l); 6 for (int i = 0;i<6;i++) 7 { 8 cout << l->data << " "; 9 l = l->next; 10 } 11 system("pause"); 12 }
运行结果:
在本题中用到了快慢指针,下面顺便详述一下快慢指针思想。
快慢指针是指指针移动的步长,快指针移动的快,慢指针移动的慢,例如可以让快指针一次移动两个步长,让慢指针一次移动一个步长。
快慢指针有两个比较重要的应用:
1、判断链表是否为单链表
2、在有序链表中寻找中位数