问题:给定一个整数数组,找到最小缓冲区值,以便可以按严格递增的顺序对数组进行排序。数组中的每个元素都可以加上或减去缓冲区值。
Ex: [4, 0, 9, -2]
最小缓冲区值为6,因为您可以将数组更改为:
[4 - (6 or 5 or 4 or 3), 0 + (-1 or 0 or 1 or 2), 9 - (6) = 3, -2 + (6) = 4]
我相信我的解决方案适用于O(N ^ 2),但我想知道我是否可以做得更好。
编辑:没关系,我的解决方案不起作用。
我的解决方案:
对于每个元素,绘制一条通过该元素的斜率为1的线。找到允许每个元素移动到线上的最小缓冲区值。跟踪获得的缓冲区值,并在最后返回最小的缓冲区值。
由于
答案 0 :(得分:3)
O(n)
可以通过逐个循环数组并保持不变量正确并跟踪当前缓冲区大小来实现:
abs((curr-prev)/2) + 1
添加到当前缓冲区)和之前/当前值(可能的最小值)。此时我们不需要关心先前的条目,因为我们正在增加缓冲区以减少先前/当前值,我们可以简单地从每个先前的值中减去abs((curr-prev)/2) + 1
,并且不变量将保持不变。一些例子让它更加清晰(粗体 - 当前,斜体 - 上一个):
I)输入:[4,0,9,-2]
完成,缓冲区= 6
II)输入:[40,31,140,131]
完成,缓冲区= 5
III)输入:[1,1,1,1]
完成,缓冲区= 2
IV)输入:[7,11,1,2,3]
完成,缓冲区= 6
V)输入:[0,1,3,-15]
直到[0,1,3]没有变化
完成,缓冲区= 10
VI)输入:[1,2,3,4]
完成,缓冲区= 0
答案 1 :(得分:2)
原始答案
我原来的答案包括以下三个步骤。该算法有效,前提是规范化数组中的max
值位于规范化数组中的min
值之前。我考虑的样本数组是[4,0,9,-2]和[0,1,3,-15]。请注意,在这两个示例数组中,max
位于min
之前。但是,如果数组中的绝对min
出现在绝对max
之前,则此算法将失败。算法失败的两个例子是[-15,1]和[40,31,140,131]。
步骤1:从该索引处的值中减去数组索引
array: 4 0 9 -2
index: -0 -1 -2 -3
----------------
normalized array: 4 -1 7 -5
步骤2:扫描规范化数组以找到最小值和最大值
max = 7
min = -5
步骤3:从最大值中减去min并除以2(必要时向上舍入)并得到你的答案
(7 - (-5)) / 2 = 6
原始答案的基本原理和缺陷
我原来的答案背后的想法是max
和min
之间的中点提供了target
值,规范化数组中的每个条目都可以被调整为命中。距离max
最远的min
和target
需要最大的调整,因此提供了答案。但是,在看到hk6279的评论后,我意识到规范化数组可能有多个targets
。例如,考虑数组[40,31,140,131]。第一步是
array: 40 31 140 131
index: -0 -1 -2 -3
-----------------
normalized: 40 30 138 128
前两个数字的target
为35(可通过+ -5调整到达)。前两个数字的target
为133(也可以通过+ -5的调整到达)。所以答案是5,对数组的调整是
array: 40 31 140 131
adjustment: -5 +5 -5 +5
-----------------
sorted: 35 36 135 136
(旁注:数组[-15,1]也有两个目标。-15的目标是-15,调整为0. 0的目标值(标准化值1)为0,调整的答案是0)。
更复杂(但仍然是O( n ))答案
步骤1:通过从该索引处的值中减去数组索引来规范化数组。此步骤与原始答案相同。
步骤2:定义数据结构以保存有关数组条目的信息。
struct Section
{
int max; // the maximum value seen in this section of the array
int min; // the minimum value seen in this section of the array
int startIndex; // the starting index for this section of the array
int endIndex; // the ending index for this section of the array
}
步骤3:在创建Section
结构数组时扫描规范化数组。 (sectionsArray
可能与normalized
数组一样长。)创建sectionsArray
的规则是
initialize the first section as { normalized[0], normalized[0], 0, 0 }
for each subsequent entry in the normalized array
{
if ( normalized[i] > currentSection.max ) // found a larger value than the current max
{
newSection = { normalized[i], normalized[i], i, i } // create a new section and add it to the sectionsArray
currentSection = newSection // the new section is now our current section
}
else if ( normalized[i] < currentSection.min ) // found a new minimum for the current section
{
currentSection.min = normalized[i] // update the min and end of the current section
currentSection.endIndex = i;
}
else // normalized[i] is within the current range of values
{
currentSection.endIndex = i; // update the end of the current section
}
}
请注意,在此步骤中,sectionsArray
中的最大值严格按升序排列。这对下一步非常重要。
步骤4:从sectionsArray
的末尾开始向后工作,尽可能组合各个部分。组合两个部分的规则是
if ( sectionsArray[i].min <= sectionsArray[i-1].min ) // bigger max and smaller min allows preceding section to be absorbed into the current section
{
sectionsArray[i-1].max = sectionsArray[i].max
sectionsArray[i-1].min = sectionsArray[i].min
sectionsArray[i-1].endIndex = sectionsArray[i].endIndex
discard sectionsArray[i]
}
第5步:扫描sectionsArray
,找出max
和min
之间的最大差异。最大的差异除以2并在必要时进行四舍五入是问题的答案。此外,min
和max
之间的中点是数组该部分的目标值(目标值可以被截断)。
示例
考虑数组[8,5,8,6,14,12,18,13]。首先规范化数组:
Input array: 8 5 8 6 14 12 18 13
index: -0 -1 -2 -3 -4 -5 -6 -7
------------------------------
Normalized: 8 4 6 3 10 7 12 6
然后创建sections数组:
{ 8, 3, 0, 3 } // started with 8, 4 became the min, 6 was in range, 3 replaced 4 as the min. 10 ended the section since it was higher than 8
{ 10, 7, 4, 5 } // started with 10, 7 became the min. 12 ended the section.
{ 12, 6, 6, 7 }
向后工作:10,7部分可以被吸收到12,6部分中,从而产生
{ 8, 3, 0, 3 }
{ 12, 6, 4, 7 }
最大差异为6,所以答案是3。
第一部分的目标是(8 + 3)/ 2 = 5(截断后) 第二部分的目标是(12 + 6)/ 2 = 9
调整是:
Normalized: 8 4 6 3 10 7 12 6
Adjustments: -3 1 -1 2 -1 2 -3 3
-------------------------------
Targets: 5 5 5 5 9 9 9 9
将相同的调整应用于输入数组:
Input array: 8 5 8 6 14 12 18 13
Adjustments: -3 1 -1 2 -1 2 -3 3
-------------------------------
Sorted: 5 6 7 8 13 14 15 16
将此技术应用于此线程中的其他数组
input: 4 0 9 -2
normalized: 4 -1 7 -5
sections: { 4, -1, 0, 1 } { 7, -5, 2, 3 }
combined: { 7, -5, 0, 3 }
answer: (7 - (-5)) / 2 = 6
targets: one target for the entire normalized array => (7 + (-5)) / 2 = 1
input: 0 1 3 -15
normalized: 0 0 1 -18
sections: { 0, 0, 0, 1 } { 1, -18, 2, 3 }
combined: { 1, -18, 0, 3 }
answer: (1 - (-18)) / 2 = 10
targets: one target for the entire normalized array => (1 + (-18)) / 2 = -8
input: -15 1
normalized: -15 0
sections: { -15, -15, 0, 0 } { 0, 0, 1, 1 }
combined: same as above
answer: 0 (same answer for both sections)
targets: targets are -15 and 0
input: 40 31 140 131
normalized: 40 30 138 128
sections: { 40, 30, 0, 1 } { 138, 128, 2, 3 }
combined: same as above
answer: 5 (same answer for both sections)
targets: targets are 35 and 133
挑战
找到一个破坏此算法的计数器示例并将其发布在评论中:)
答案 2 :(得分:1)
您可以使用二进制搜索来解决此问题。
所以假设缓冲区的大小是x =(低+高)/ 2,你可以做的是从每个索引从0到n - 1(n是数字元素),你只需要需要计算什么是最小值 它可以使用缓冲区x(条件是它应该大于最后一个元素)。这种贪婪将帮助您验证查找x是否可以成为有效的解决方案。
例如,如果x = 6,则数组为[4,0,9,-2]
index 0, min is 4 - 6 = -2
index 1, min is 0 - 1 = -1 (as we need to make this greater than -2)
index 2, min is 9 - 6 = 3
index 3, min is -2 + 6 = 4
所以,6是有效的。
伪代码:
int low = 0;
int high = //n times Max absolute value in the array
while(low <- high){
int x = (low + high)/2
if(x make the array become sorted)
update min result;
high = x - 1;
else
low = x + 1;
}
答案 3 :(得分:1)
这是O(N)
解决方案。
对于相邻的一对,例如a
和b
,如果发生a > b
,我们需要调整它们。要按升序调整它们,我们需要找到X
a - X < b + X
,这相当于找到满足X
的{{1}}。因此,我们只需要扫描一次数组,找到a - b < 2X
对,并计算a > b
,并回答最大值。
有效的实现可以写成如下:
ceil((a - b) / 2)