题目:
Sort a linked list in O(n log n) time using constant space complexity.
题意:
用O(nlogn)的时间复杂度和一个O(1)的空间复杂度来对一个单链表进行排序。
题解:
因为对时间复杂度和空间复杂度都有要求,对照了各种排序算法,发现只有归并排序才能满足题目的要求。所以考虑用归并排序来做。首先就要了解什么是归并排序。归并排序中最常用的就是二路归并,也就是两两归并,我们采用分别找到中间节点,然后就是归并操作了。其中归并排序分别有对数组和单链表的操作,首先我们可以看到对这两个数据结构进行操作,因为此题是单链表,而单链表由于只能顺序地访问,所以我们不能采用其他的堆排序和快排,会非常麻烦,所以只能用归并排序比较好;另外,归并排序的要点在于递归地访问中间节点,这也是归并排序的重点。下面分别写了对于单链表和数组的归并排序算法:
public ListNode sortList(ListNode head)
{
if(head == null || head.next == null)
return head;
ListNode p = head;
ListNode f = head.next.next;
if(f != null) //首先用两个指针,其中第二个指针比第一个指针每一步快走两步,那么这样第二个指针走到末尾,第一个指针就指到中间节点
{
while(f.next != null && f.next.next != null)
{
p = p.next;
f= f.next.next;
}
}
ListNode h2 = sortList(p.next); //对左边的单链表进行归并
p.next = null;
return merge(sortList(head),h2); //合并两个单链表
}
public ListNode merge(ListNode h1,ListNode h2) //合并单链表
{
ListNode hn = new ListNode(-1);
ListNode c = hn;
while(h1 != null && h2 != null)
{
if(h1.val <= h2.val)
{
c.next = h1;
h1 = h1.next;
}
else
{
c.next = h2;
h2 = h2.next;
}
c = c.next;
}
if(h1 == null)
c.next = h2;
else
c.next = h1;
return hn.next;
}
下面是对数组的归并排序:
public void sort(int[] data,int left,int right)
{
if(left >= right)
return;
int center = (left + right) / 2; //数组只要有下标就行了
sort(data,left,center); //递归
sort(data,center+1,right); //递归
merge(data,left,center,right); //合并
}
public void merge(int[] data,int left,int center,int right) //合并
{
int[] tmpArr = new int[data.length];
int mid = center + 1;
int third = left;
int tmp = left;
while(left <= center && mid <= right)
{
if(data[left] <= data[mid])
tmpArr[third++] = data[left++];
else
tmpArr[third++] = data[mid++];
}
while(mid <= right)
{
tmpArr[third++] = data[mid++];
}
while(left <= center)
tmpArr[third++] = data[left++];
while(tmp <= right)
data[tmp] = tmpArr[tmp++];
}