我遇到了以下sample job interview question。我该如何解决?
假设我们有一个数组 a1,a2,...,an,b1,b2,...,bn。
目标是将此数组更改为 在O(n)时间和O(1)空间中的a1,b1,a2,b2,...,a,bn。 换句话说,我们需要一个线性时间算法来修改数组,只需要一定量的额外存储空间。
答案 0 :(得分:11)
这个问题并不像人们想象的那样微不足道。家庭作业? LOL。
以下链接有一个解决方案:http://arxiv.org/abs/0805.1598
答案 1 :(得分:1)
这个问题并不像看起来那么容易,但经过一番思考后,实现这一目标的算法并不算太糟糕。你会注意到第一个和最后一个元素已经存在,所以我们不需要担心它们。我们将保留一个左索引变量,它表示数组前半部分中需要更改的第一项。之后,我们将一个正确的索引变量设置为需要更改的数组的后半部分中的第一个项目。现在我们所做的就是将右侧索引处的项目逐个交换,直到它到达左侧索引项。将左侧索引增加2并将右侧索引增加1,并重复直到索引重叠或左侧超过右侧索引(右侧索引将始终在阵列的最后一个索引处结束)。我们每次都将左侧索引增加2,因为左侧+ 1的项目已经自然地落到了位置。
protected void Interleave(int[] arr)
{
int left = 1;
int right = arr.Length / 2;
int temp;
while (left < right)
{
for (int i = right; i > left; i--)
{
temp = arr[i];
arr[i] = arr[i - 1];
arr[i - 1] = temp;
}
left += 2;
right += 1;
}
}
这个算法使用O(1)存储(使用临时变量,可以使用加/减速交换技术消除)我不是很擅长运行时分析,但我相信这仍然是O(n)甚至虽然我们正在进行许多交换。也许有人可以进一步探索其运行时分析。
答案 2 :(得分:1)
它被称为in-place in-shuffle problem。以下是基于here的C ++实现。
void in_place_in_shuffle(int arr[], int length)
{
assert(arr && length>0 && !(length&1));
// shuffle to {5, 0, 6, 1, 7, 2, 8, 3, 9, 4}
int i,startPos=0;
while(startPos<length)
{
i=_LookUp(length-startPos);
_ShiftN(&arr[startPos+(i-1)/2],(length-startPos)/2,(i-1)/2);
_PerfectShuffle(&arr[startPos],i-1);
startPos+=(i-1);
}
// local swap to {0, 5, 1, 6, 2, 7, 3, 8, 4, 9}
for (int i=0; i<length; i+=2)
swap(arr[i], arr[i+1]);
}
// cycle
void _Cycle(int Data[],int Lenth,int Start)
{
int Cur_index,Temp1,Temp2;
Cur_index=(Start*2)%(Lenth+1);
Temp1=Data[Cur_index-1];
Data[Cur_index-1]=Data[Start-1];
while(Cur_index!=Start)
{
Temp2=Data[(Cur_index*2)%(Lenth+1)-1];
Data[(Cur_index*2)%(Lenth+1)-1]=Temp1;
Temp1=Temp2;
Cur_index=(Cur_index*2)%(Lenth+1);
}
}
// loop-move array
void _Reverse(int Data[],int Len)
{
int i,Temp;
for(i=0;i<Len/2;i++)
{
Temp=Data[i];
Data[i]=Data[Len-i-1];
Data[Len-i-1]=Temp;
}
}
void _ShiftN(int Data[],int Len,int N)
{
_Reverse(Data,Len-N);
_Reverse(&Data[Len-N],N);
_Reverse(Data,Len);
}
// perfect shuffle of satisfying [Lenth=3^k-1]
void _PerfectShuffle(int Data[],int Lenth)
{
int i=1;
if(Lenth==2)
{
i=Data[Lenth-1];
Data[Lenth-1]=Data[Lenth-2];
Data[Lenth-2]=i;
return;
}
while(i<Lenth)
{
_Cycle(Data,Lenth,i);
i=i*3;
}
}
// look for 3^k that nearnest to N
int _LookUp(int N)
{
int i=3;
while(i<=N+1) i*=3;
if(i>3) i=i/3;
return i;
}
<强>测试强>
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int length = sizeof(arr)/sizeof(int);
in_place_in_shuffle(arr, length);
在此之后,arr[]
将为{0, 5, 1, 6, 2, 7, 3, 8, 4, 9}
。
答案 3 :(得分:0)
首先,理论:重新排列'置换周期'中的元素。取一个元素并将其放置在新位置,替换当前存在的元素。然后你取出那个被取代的元素并将它置于新的位置。这取代了另一个元素,所以冲洗并重复。如果移位的元素属于您首先使用的元素的位置,则表示您已完成一个循环。
实际上,你问我here这个问题的特例是:在O(N)时间和O(1)空间中如何将数组重新排列到任何给定的顺序?在我的问题中,重新排列的位置由一组数字描述,其中第n个位置的数字指定原始数组中元素的索引。
但是,在您的问题中没有这个额外的数组,并且分配它将需要O(N)空间。幸运的是,我们可以动态计算此数组中任何元素的值,如下所示:
int rearrange_pos(int x) {
if (x % 2 == 0) return x / 2;
else return (x - 1) / 2 + n; // where n is half the size of the total array
}
我不会在这里复制重新排列算法本身;它可以在我的问题的接受答案中找到。
编辑:正如Jason指出的那样,我链接到的答案仍然需要分配一个bool数组,使其成为O(N)空间。这是因为置换可以由多个周期组成。我一直试图为你的特殊情况消除对这个阵列的需求,但没有成功..似乎没有任何可用的模式。也许其他人可以帮助你。
答案 4 :(得分:-3)
如果您可以先将数组转换为链接列表,那么问题就变得微不足道了。