我最近在采访中被问到这个问题。
给定一个包含负数和正数的排序整数数组,如何根据元素的绝对值求助数组?
这必须严格按 O(n)时间进行。
输入
{ - 9,-7,-3,1,6,8,14}
输出
{1,-3,6,-7,8-,-9,14}
除了O(n)时间以外,有哪些可能的解决方案?
答案 0 :(得分:12)
基本上,我们将有两个头,一个看着阵列的末端,一个在开头。
v v
{-9, -7, -3, 1, 6, 8, 14}
我们比较我们的头指向的2个条目的绝对值,并将更大的值插入到我们新的排序数组中。所以这里将是14。
New array: {14}
然后我们移动我们选择的项目的头部靠近中心。所以在这里我们将头部指向14到8。
v v
{-9, -7, -3, 1, 6, 8, 14}
然后我们重复这个过程,将2个绝对值中较大的一个插入到新的排序数组的开头。这里是-9,为| -9 | > | 8 |
New array: {-9, 14}
在我们再次移动头部之后:
v v
{-9, -7, -3, 1, 6, 8, 14}
重复直到两个脑袋在中心相遇
答案 1 :(得分:2)
让我们举个例子:
{-9,-7,-3,1,6,8,14}
您可以将其重写为两部分:
{-9,-7,-3} and {1,6,8,14}
两个明显的观察结果:
现在你基本上只是合并这两个排序的子阵列,这可能是线性时间。在这里查看算法How to merge two sorted arrays into a sorted array?。
由于您没有将这些阵列拆分,您会发现数组中的点向负数转为正数。从该分割点开始,您可以逐个索引,直到数组的边界,并再次重构排序的数组。
答案 2 :(得分:1)
如评论中所述,您基本上是在查看合并排序。原始数据已经排序,因此您所要做的就是从每组负数和正数中按顺序检索值,并根据它们的绝对值合并它们。
代码看起来像这样:
int[] input = { -9, -7, -3, 1, 6, 8, 14 };
int[] output = new int[input.Length];
int negativeIndex, positiveIndex = 0, outputIndex = 0;
while (input[positiveIndex] < 0)
{
positiveIndex++;
}
negativeIndex = positiveIndex - 1;
while (outputIndex < output.Length)
{
if (negativeIndex < 0)
{
output[outputIndex++] = input[positiveIndex++];
}
else if (positiveIndex >= input.Length)
{
output[outputIndex++] = input[negativeIndex--];
}
else
{
output[outputIndex++] = -input[negativeIndex] < input[positiveIndex] ?
input[negativeIndex--] : input[positiveIndex++];
}
}
以上是恕我直言,有点容易掌握,但正如另一个答案所指出的,你可以做到这一点,甚至没有扫描到数据的0点,从另一端排序。代码看起来像这样:
int[] output = new int[input.Length];
int negativeIndex = 0, positiveIndex = input.Length -1, outputIndex = input.Length - 1;
while (outputIndex >= 0)
{
if (input[negativeIndex] >= 0)
{
output[outputIndex--] = input[positiveIndex--];
}
else if (input[positiveIndex] < 0)
{
output[outputIndex--] = input[negativeIndex++];
}
else
{
output[outputIndex--] = -input[negativeIndex] < input[positiveIndex] ?
input[positiveIndex--] : input[negativeIndex++];
}
}