最近我在面试中被问到了这个问题。
给定一个包含负数和正数的已排序整数数组,如何根据元素的绝对值重新排列数组?
必须在O(n)时间内完成。
输入
{-9,-7,-3,1,6,8,14}
输出
{1,-3,6,-7,8,-9,14}
除了O(n)时间,还有哪些可能的解决方案?
最近我在面试中被问到了这个问题。
给定一个包含负数和正数的已排序整数数组,如何根据元素的绝对值重新排列数组?
必须在O(n)时间内完成。
输入
{-9,-7,-3,1,6,8,14}
输出
{1,-3,6,-7,8,-9,14}
除了O(n)时间,还有哪些可能的解决方案?
基本上,我们将有两个指针,一个指向数组的末尾,另一个指向开头。
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}
然后我们重复这个过程,将绝对值较大的那个数插入到新的已排序数组的开头。在这里,绝对值较大的数是-9,因为|-9| > |8|
New array: {-9, 14}
然后我们再次移动头部:
v v
{-9, -7, -3, 1, 6, 8, 14}
反复执行直到两个头在中心相遇
让我们以你的例子为例:
{-9,-7,-3,1,6,8,14}
{-9,-7,-3} and {1,6,8,14}
两个明显的观察点:
现在基本上只需合并这两个已排序子数组,可能需要线性时间。可以查看此处的算法如何将两个已排序的数组合并成一个已排序的数组?。
由于您没有将这些数组拆分,因此需要找到数组中翻转为正值的点。从该分割点开始,您可以一次索引一个地重构已排序的数组,直到数组的边界。
代码应该如下所示:
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++];
}
}
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++];
}
}