2.2.12次線性的額外空間。用大小M將數組分爲N/M塊(簡單起見,設M是N的約數)。實現一個歸併方法,使之所需的額外空間減小到max(M,N/M):(i)能夠先將一個塊看作一個元素,將塊的第一個元素做爲塊的主鍵,用選擇排序將塊排序;(ii)遍歷數組,將第一塊和第二塊歸併,完成後將第二塊和第三塊歸併,等等。
答:一個O(N^2)時間複雜度,O(M)空間複雜度的算法。
1)將a劃分紅N/M個單個長度最長爲M的子數組,對每一個子數組使用歸併排序算法排序。
2)將a劃分紅N/M個單個長度最長爲M子數組,以每一個子數組的第一個元素做爲排序關鍵字,使用選擇排序算法將各個子數組排序。對N/M個子數組排序,在歸併時會因歸併的兩段已有序而不用再歸併。
3)歸併子數組1和子數組2,再歸併子數組1~2和子數組3,再歸併子數組1~3和子數組4,直到歸併完子數組1~(N/M-1)和子數組N/M。例以下現的狀況(2,20,30)(3,5,8)(4,6,10),歸併前兩個後爲(2,3,5)(8,20,30)(4,6,10),此時若只歸併後面兩個子組數,歸併後的結果是(2,3,5)(4,6,8)(10,20,30)此時是無序的。若是在子數組1,2歸併後,再將1,2與子數組3歸併,獲得的結果(2,3,4)(5,6,8)(10,20,30)纔有序,因此此種採用了後段與全部前段歸併的方式,也所以造成O(N^2)時間複雜度,但也能夠當作是O((N/M)^2)時間複雜度。
4)若是歸併的兩段的交界值有序,那麼跳過本次歸併,無序時歸併這兩段。而後進行下一段與全部前段的歸併。歸併時先將後段長度爲M的子數組複製到輔助數組aux,而後將前段和輔組數組進行歸併,兩段中的大值從數組a的右邊歸併到左邊。
public class E2d2d12
{
public static void sort(Comparable[] a,int M)
{
int N=a.length;
Comparable[] aux=new Comparable[M];
//sort each block elements, block size is 2*M
for(int lo=0;lo<N;lo=lo+M)
sort(a,aux,lo,lo+M-1);
//sort all blocks with block first element
SelectionSortBlock(a,M);
//Merge
for(int j=1;j<N/M;j++)
{
if (!less(a[j*M],a[j*M-1])) continue;
Merge(a,aux,0,j*M-1,(j+1)*M-1);
}
}
private static void sort(Comparable[] a,Comparable[] aux,int lo,int hi)
{
if (hi<=lo) return;
int mid=lo+(hi-lo)/2;
sort(a,aux,lo,mid);
sort(a,aux,mid+1,hi);
if (!less(a[mid+1],a[mid])) return;
Merge(a,aux,lo,mid,hi);
}
//merge bigger value to the maxIndex of array a.
private static void Merge(Comparable[] a,Comparable[] aux,int lo,int sp,int hi)
{
int M=hi-sp;
//copy right half of array a to aux
for(int i=0;i<M;i++)
aux[i]=a[sp+1+i];
//merge from array aux with left half of a to a
int auxTop=M-1;
int aTop=sp;
for(int k=hi;k>=lo;k--)
{
if(auxTop<0) a[k]=a[aTop--];
else if (aTop<lo) a[k]=aux[auxTop--];
else if(less(aux[auxTop],a[aTop])) a[k]=a[aTop--];
else a[k]=aux[auxTop--];
}
}
private static void SelectionSortBlock(Comparable[] a,int M)
{
int length=a.length;
int minIndex;
for(int i=0;i<length;i=i+M)
{
minIndex=i;
for(int j=i+M;j<length;j=j+M)
if(less(a[j],a[minIndex])) minIndex=j;
exch(a,i,minIndex,M);
}
}
private static boolean less(Comparable v,Comparable w)
{
return v.compareTo(w)<0;
}
private static void exch(Comparable[] a,int i,int j,int length)
{
Comparable t;
for(int index=0;index<length;index++)
{
t=a[i+index];
a[i+index]=a[j+index];
a[j+index]=t;
}
}
public static boolean isSorted(Comparable[] a)
{
int len=a.length;
for(int i=1;i<len;i++)
if (less(a[i],a[i-1])) return false;
return true;
}
public static void main(String[] args)
{
int N=Integer.parseInt(args[0]);
int M=Integer.parseInt(args[1]);
Double[] a=new Double[N];
for(int i=0;i<N;i++)
a[i]=StdRandom.uniform();
sort(a,M);
StdOut.printf("isSorted=%s",isSorted(a));
}
}
問題中的max(M,N/M)沒有用到、將第一塊和第二塊歸併,完成後將第二塊和第三塊歸併等等沒有用到,塊排序對第一塊和第二塊,第二塊對第三塊的歸併的意義有沒有多是將時間複雜度降底到O(NlgN)的一個前提保證?若是是應該怎麼使用?很遺憾,可是下面的內容說明有更好的時間複雜度算法。 算法