对于长度为m+n的数组(m,n均为正整数),编制程序,将数组的前m个数与后n个数进行变换,要求不能使用另外的数组。
例如,对于m=4,n=6,长为10的数组:(0 1 2 3 4 5 6 7 8 9),变换后的结果为:(4, 5, 6, 7, 8, 9, 0, 1, 2, 3)。
通过m次循环左移或n次循环右移可以完成数组元素交换任务,每次循环移位需执行m+n+1次赋值操作。当m>n时,右移较左移为佳;当m<n时,左移较右移为佳。因此,当m>n时,进行右移;当m<n时,进行左移;m=n时,两者相同。程序的时间复杂性为min(m,n)*(m+n+1)。
void CircleSwap(int *lpItem, int nHeadElements, int nTailElements)
{
int nShift;
int nSize = nHeadElements + nTailElements;
if (nHeadElements > nTailElements)
for (nShift = 0; nShift < nTailElements; nShift++)
CircleRightShift(lpItem, nSize);
else
for (nShift = 0; nShift < nHeadElements; nShift++)
CircleLeftShift(lpItem, nSize);
}
void CircleRightShift(int *lpItem, int nSize)
{
int nTmpElement;
int nIndex;
nTmpElement = *(lpItem + nSize - 1);
for (nIndex = nSize - 1; nIndex > 0; nIndex--)
*(lpItem + nIndex) = *(lpItem + nIndex - 1);
*lpItem = nTmpElement;
}
void CircleLeftShift(int *lpItem, int nSize)
{
int nTmpElement;
int nIndex;
nTmpElement = *lpItem;
for (nIndex = 0; nIndex < nSize - 1; nIndex++)
*(lpItem + nIndex) = *(lpItem + nIndex + 1);
*(lpItem + nSize - 1) = nTmpElement;
}
以前面的例子来说明。
第一步:将前面四个元素与后面六个元素中的前四个元素(4~7)对换。结果为
(4,5,6,7,0,1,2,3,8,9)
这时,问题变为将数组元素4~7与8~9交换位置。
第二步:将4~7中的前面两个元素与后面两个元素8~9对换。结果为
(4,5,6,7,8,9,2,3,0,1)。
第三步:剩下的任务是将元素6~7与8~9进行对换。结果为
(4,5,6,7,8,9,0,1,2,3)
上述算法可以递归地描述为:将数组中start开始的m+n个元素中的前m个与后n个交换位置:
BlockSwap(int *a, int start, int m, int n)
{
int i;
switch (sgn(m-n))
{
case -1:
for (i=start; I <start+m; i++)
{
t=a[i]; a[i]=a[i+m]; a[i+m]= t;
}
BlockSwap(a, start+m, m, n-m);
break;
case 0:
for (i=start; I <start+m; i++)
{
t=a[i]; a[i]=a[i+m]; a[i+m]= t;
}
break;
case 1:
for (i=start; I <start+n; i++)
{
t=a[i]; a[i]=a[i+m]; a[i+m]= t;
}
BlockSwap(a, start+n, m-n, n);
break;
}
}
两个大小为b的块进行对换,需执行3b次赋值操作。当交换的两个块一个大小为b[0],另一块大小为b[1]时,则执行q[1]次大小为b[1]的块对换操作,q[1]由下式确定:
b[0]=q[1]b[1]+b[2] (0≤b[2]<b[1])
若b[2]≠0,则需进一步执行大小为b[1]、b[2]的两个块的交换任务,相应地,应执行q[2]次大小为b[2]的块对换操作,q[2]由下式确定:
b[1]=q[2]b[2]+b[3] (0≤b[3]<b[2])
这样下去,一直到某个b[k]|b[k-1]为止。上述操作中确定{q[i]}的表达式如下所示:
b[0]=q[1]b[1]+b[2] (0≤b[2]<b[1])
b[1]=q[2]b[2]+b[3] (0≤b[3]<b[2])
......
b[i-1]=q[i]b[i]+b[i+1] (0≤b[i+1]<b[i] ......
b[k-2]=q[k-1]b[k-1]+b[k] (0≤b[k]<b[k-1])
b[k-1]=q[k]b[k]
式中b[i-1]=q[i]b[i]+b[i+1]表示进行q[i]次大小为b[i]的块对换操作,这一过程实质上等同于辗转相除法求最大公约数的过程,其中b[k]=(b[0],b[1])。
于是,块对换算法总的赋值操作次数为
3(q[1]b[1]+q[2]b[2]+...+q[k]b[k])=3(b[0]+b[1]-b[k])
由于在这一问题中,b[0]=max(m,n),b[1]=min(m,n),b[k]=(b[0],b[1]),从而,块对换算法赋值操作执行次数为3(m+n-(m,n))。
实际的程序如下:
void BlockSwap(int *lpItem, int nHeadElements, int nTailElements)
{
int *lpCurrItem;
lpCurrItem = lpItem;
while (nHeadElements != nTailElements)
if (nHeadElements > nTailElements)
{
PairsSwap(lpCurrItem, lpCurrItem + nHeadElements, nTailElements);
lpCurrItem += nTailElements;
nHeadElements -= nTailElements;
}
else
{
PairsSwap(lpCurrItem, lpCurrItem + nHeadElements, nHeadElements);
lpCurrItem += nHeadElements;
nTailElements -= nHeadElements;
}
PairsSwap(lpCurrItem, lpCurrItem + nHeadElements, nHeadElements);
}
void RecursionBlockSwap(int *lpItem, int nHeadElements, int nTailElements)
{
if (nHeadElements > nTailElements)
{
PairsSwap(lpItem, lpItem + nHeadElements, nTailElements);
RecursionBlockSwap(lpItem + nTailElements,
nHeadElements - nTailElements,
nTailElements);
}
else
if (nHeadElements < nTailElements)
{
PairsSwap(lpItem, lpItem + nHeadElements, nHeadElements);
RecursionBlockSwap(lpItem + nHeadElements,
nHeadElements,
nTailElements - nHeadElements);
}
else
PairsSwap(lpItem, lpItem + nHeadElements, nHeadElements);
}
void PairsSwap(int *lpItem1, int *lpItem2, int nSize)
{
int nIndex;
for (nIndex = 0; nIndex < nSize; nIndex++)
ElementPairSwap(lpItem1 + nIndex, lpItem2 + nIndex);
}
void ElementPairSwap(int *lpItem1, int *lpItem2)
{
int nElement;
nElement = *lpItem1;
*lpItem1 = *lpItem2;
*lpItem2 = nElement;
}
设原数组为a[0]a[1]…a[m+n-1],变换后的结果为b[0]b[1]…b[m+n-1]。显然,b[i]=a[(i+m)%(m+n)],即第i个位置被(i+m)%(m+n)个元素占据。
记[i]=a[(i+m)%(m+n)],则k个元素[i]、[i+m]、…、[i+(k-1)m]构成一个换位循环,即第i个位置由[i+m]占据,第(i+m)%(m+n)个位置由[(i+2m)]占据,…,第(i+(k-1)m)%(m+n)个位置由[i]占据。这里k=(m+n)/(m,n)。
为证明上述命题,只需证明满足i=(i+km)%(m+n)最小的正整数k为(m+n)/(m,n)即可。
不妨设km=q(m+n),(m,n)=g,m=gm0,n=gn0,从而km0=q(m0+n0)
由于(m0,n0)=1,则(m0+n0,m0)=1,故m0+n0|k, 由k的最小性,k=m0+n0=(m+n)/(m,n)。
设x是一个换位循环中下标最小的数组元素的下标,换位循环中的全体元素构成的集合记为{x}。
命题:(m,n)个换位循环{0}、{1}、…、{(m,n)-1}构成a[0]a[1]…a[m+n-1]的一个划分。即数组中的任一元素均在一个换位循环中,且没有两个不同的循环中有相同的数组元素。
证明:用反证法。设存在一个数组元素同时属于两个不同的换位循环{x}、{y}((m,n)>x,y>=0),则存在i、j,使得
(x+im)%(m+n)=(y+jm)%(m+n)
即存在q使得
x-y=q(m+n)+(j-i)m
从而
(m,n)|x-y
由于1<=x-y<(m,n),这是不可能的,故不存在两个换位循环有公共元素。又(m,n)个换位循环包括(m,n)(m+n)/(m,n)=m+n个元素,因此数组中任一元素只在一个换位循环中。
每一换位循环执行赋值操作(m+n)/(m,n)+1次,程序共执行(m,n)次换位循环,故程序的时间复杂性为m+n+(m,n)。
程序如下:
void BestSwap(int *lpItem, int nHeadElements, int nTailElements)
{
int nSize = nHeadElements + nTailElements;
int nShiftGroup, nGroupCount;
int nStartIndex, nIndex, nNextIndex;
int nTmpElement;
nGroupCount = gcd(nHeadElements, nTailElements);
for (nShiftGroup = 0; nShiftGroup < nGroupCount; nShiftGroup++)
{
nStartIndex = nShiftGroup;
nTmpElement = *(lpItem + nStartIndex);
nIndex = nStartIndex;
nNextIndex = nIndex + nHeadElements;
if (nNextIndex >= nSize) nNextIndex -= nSize;
while (nNextIndex != nStartIndex)
{
*(lpItem + nIndex) = *(lpItem + nNextIndex);
nIndex = nNextIndex;
nNextIndex = nIndex + nHeadElements;
if (nNextIndex >= nSize) nNextIndex -= nSize;
}
*(lpItem + nIndex) = nTmpElement;
}
}
// Using the Euclidean algorithm to calculate the greatest common divisor of two integers
int gcd(int a, int b)
{
int r;
if (a < 0) a = -a;
if (b < 0) b = -b;
while (b != 0)
{
r = a % b; a = b; b = r;
}
return a;
}
因篇幅问题不能全部显示,请点此查看更多更全内容