这道题是用自底向上的归并排序,要利用子数组的有序性。我们要先找到两个有序的子数组,将其归并后,再往后寻找下一个有序的子数组,与前面归并后的数组再次归并:
// 原地归并
public static void merge(Comparable[] a, int lo, int mid, int hi)
{
// 将a[lo..mid]和a[mid+1..hi]归并
int i = lo, j = mid + 1;
//Comparable[] aux = new Comparable[a.length];
Comparable[] aux = new Comparable[a.length];
// 将a[lo..hi]复制到aux[lo..hi]
for (int k = lo; k <= hi; k++)
{
aux[k] = a[k];
}
// 归并回到a[lo..hi]
for (int k = lo; k <= hi; k++)
{
if (i > mid) {
a[k] = aux[j++];
} else if (j > hi) {
a[k] = aux[i++];
} else if (less(aux[j], aux[i])) {
a[k] = aux[j++];
} else {
a[k] = aux[i++];
}
}
StdOut.println();
//StdOut.println("hi: " + hi + ", lo: " + lo);
}
public static void sort(Comparable[] a)
{
int n = a.length;
int lo = 0;
int mid = -1;
int hi = -1;
while (hi < n - 1)
{
for (int i = hi + 1; i < n; i++)
{
if ((i + 1) < a.length)
{
if (!less(a[i], a[i + 1])) {
if (mid == -1) {
mid = i;
} else {
hi = i;
break;
}
}
}
else // 已到末尾
{
if (mid == -1) {
mid = i;
}
hi = i;
}
}
if (mid != hi) {
merge(a, lo, mid, hi);
StdOut.println("mid: " + mid + ", hi: " + hi);
}
mid = hi;
}
StdOut.println();
}主要是sort方法,lo表示其实索引,mid表示中间索引,hi表示末尾索引。
int n = a.length;
int lo = 0;
int mid = -1;
int hi = -1;
在第一次循环时,确定mid,hi,确定hi后,跳出循环:
for (int i = hi + 1; i < n; i++)
{
if ((i + 1) < a.length)
{
if (!less(a[i], a[i + 1])) {
if (mid == -1) {
mid = i;
} else {
hi = i;
break;
}
}
}
else // 已到末尾
{
if (mid == -1) {
mid = i;
}
hi = i;
}
}后续的mid在每次的while循环中,设置等于上一次的hi,当mid等于hi时,表示已经是有序的了,不用归并。
while (hi < n - 1)
{
for (int i = hi + 1; i < n; i++)
{
if ((i + 1) < a.length)
{
if (!less(a[i], a[i + 1])) {
if (mid == -1) {
mid = i;
} else {
hi = i;
break;
}
}
}
else // 已到末尾
{
if (mid == -1) {
mid = i;
}
hi = i;
}
}
if (mid != hi) {
merge(a, lo, mid, hi);
StdOut.println("mid: " + mid + ", hi: " + hi);
}
mid = hi;
}至于【根据数组大小和数组中递增的子数组的最大长度分析算法运行时间】:
前面已排序的子数组的大小越大,所需时间也越长。
O(nLogn)。
对于时间复杂度,我还不是特别清楚。