即我们需要找出链表中的两个有序链表段,lo, mid, hi
其中lo..mid是有序的,mid+1..hi也是有序的。将两个有序段排序,并继续向后查找第三个有序段,依此类推,类似2.2.16题。
为了方便起见,我定义了一个LoMidHi类,它包含lo, mid, hi三个属性。
要归并排序链表,经过思索,i最初指向lo, j指向mid.next,i > mid或j > hi时,排序完成。
merge方法如下:
/// 归并排序,会将正确设置下一次归并排序的r.mid
private static void merge(ListNode list, LoMidHi r)
{
NodeStr i = r.lo;
NodeStr j = r.mid.getNext();
NodeStr prev_i = null;
NodeStr prev_j = r.mid;
NodeStr nextJ = r.hi.getNext();
// 对于是否排序完成,i > mid 或 j > r.hi
boolean is_end = false;
while (!is_end)
{
// 当j小于i的值时,
if (less(j.getItem(), i.getItem()))
{
// 将j插入到i之前
if (prev_i != null)
{
prev_i.setNext(j);
}
else
{
r.lo = j;
list.setFirst(j);
}
// 记录j的下一个指向的元素
// 将prev_j(r.mid)指向j的下一个元素,将j插入到i之前,并设置它们前后的引用
prev_j.setNext(j.getNext());
j.setNext(i);
prev_i = j;
// 移动j的值,其实prev_j不会改变,
// 因为i之前和j之后的都是有序的
j = prev_j.getNext();
// 当j超出hi界限后,代表我们的j是移动过的,
// 且mid+1..hi的一定都移动到了mid+1之前,所以我们的r.mid不变
// 我们将r.hi指向r.mid
// 这里已经完全排序,我们要跳出循环
if (j == nextJ)
{
is_end = true;
r.hi = r.mid;
}
}
else
{
prev_i = i;
i = i.getNext();
// 当i > mid时,因为mid+1..hi都是有序的
// 所以此时全部排序完成,将r.mid设置为r.hi
if (prev_i == r.mid)
{
r.mid = r.hi;
is_end = true;
}
}
}
}sort方法:
private static void sort(ListNode list)
{
LoMidHi r = fillLoMidHi(list);
while (r.mid != r.hi)
{
merge(list, r);
NodeStr next = r.mid.getNext();
if (next != null)
{
while (next.getNext() != null)
{
if (less(next.getItem(), next.getNext().getItem()))
{
next = next.getNext();
}
else
{
break;
}
}
r.hi = next;
}
else
{
break;
}
}
StdOut.println();
}