取自谷歌面试问题here
假设您有一个排序的整数数组(正数或负数)。您希望将形式为f(x)= a * x ^ 2 + b * x + c的函数应用于数组的每个元素x,以便仍然对结果数组进行排序。用Java或C ++实现它。输入是初始排序数组和函数参数(a,b和c)。
你认为我们可以在小于O(n log(n))时间的情况下就地进行,其中n是数组大小(例如,在对数组进行排序后,将函数应用于数组的每个元素)?
答案 0 :(得分:1)
二次变换可能导致部分值“折叠”到其他值。你必须改变他们的顺序,这可以很容易就地完成,但是你需要合并这两个序列。
线性时间内的就地合并是可能的,但这是一个困难的过程,通常超出了面试问题的范围(除非教师在Algorithmics中的位置)。
看一下这个解决方案:http://www.akira.ruc.dk/~keld/teaching/algoritmedesign_f04/Artikler/04/Huang88.pdf
我想主要的想法是保留数组的一部分,允许交换对其包含的数据进行加扰。您可以使用它在阵列的其余部分上执行部分合并,最后对数据进行排序。 (合并缓冲区必须足够小,以便不需要超过O(N)来对其进行排序。)
答案 1 :(得分:1)
如果a是&gt; 0,然后在x = -b /(2a)处出现最小值,并且值将从[0]到[n-1]按正向顺序复制到输出数组。如果&lt; 0,然后在x = -b /(2a)处出现最大值,并且值将以[n-1]到[0]的相反顺序复制到输出数组。 (如果a == 0,那么如果b> 0,则执行正向复制,如果b <0,则执行反向复制,如果a == b == 0,则不需要执行任何操作)。我认为排序的数组可以二进制搜索O(log2(n))中最接近-b /(2a)的值(否则它是O(n))。然后将此值复制到输出数组,并将之前的值(递减索引或指针)和之后的值(递增索引或指针)合并到输出数组中,花费O(n)时间。
答案 2 :(得分:0)
我认为这可以在线性时间内完成。因为函数是二次函数,它将形成抛物线,即值减小(假设&#39; a&#39;的正值)下降到某个最小点,然后在此之后将增加。所以算法应该遍历排序的值,直到我们达到/传递函数的最小点(可以通过简单的微分来确定),然后对于每个值,在最小值之后,它应该向后走过前面的值,寻找插入该值的正确位置。使用链接列表可以允许项目就地移动。
答案 3 :(得分:0)
static void sortArray(int arr[], int n, int A, int B, int C)
{
// Apply equation on all elements
for (int i = 0; i < n; i++)
arr[i] = A*arr[i]*arr[i] + B*arr[i] + C;
// Find maximum element in resultant array
int index=-1;
int maximum = -999999;
for (int i = 0; i< n; i++)
{
if (maximum < arr[i])
{
index = i;
maximum = arr[i];
}
}
// Use maximum element as a break point
// and merge both subarrays usin simple
// merge function of merge sort
int i = 0, j = n-1;
int[] new_arr = new int[n];
int k = 0;
while (i < index && j > index)
{
if (arr[i] < arr[j])
new_arr[k++] = arr[i++];
else
new_arr[k++] = arr[j--];
}
// Merge remaining elements
while (i < index)
new_arr[k++] = arr[i++];
while (j > index)
new_arr[k++] = arr[j--];
new_arr[n-1] = maximum;
// Modify original array
for (int p = 0; p < n ; p++)
arr[p] = new_arr[p];
}