什么是最小缓冲区值,使得一组int将按递增顺序排序?

时间:2014-09-05 04:41:43

标签: arrays performance algorithm list sorting

问题:给定一个整数数组,找到最小缓冲区值,以便可以按严格递增的顺序对数组进行排序。数组中的每个元素都可以加上或减去缓冲区值。

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的线。找到允许每个元素移动到线上的最小缓冲区值。跟踪获得的缓冲区值,并在最后返回最小的缓冲区值。

由于

4 个答案:

答案 0 :(得分:3)

O(n)可以通过逐个循环数组并保持不变量正确并跟踪当前缓冲区大小来实现:

  1. 我们从左边开始,一个接一个地走到右边
  2. 单个元素子阵列按定义排序
  3. 使用当前缓冲区将当前元素调整到最低可能值(因此不变量仍然保持不变)
  4. 如果当前元素(调整后)大于前一个元素,那么我们不需要做任何事情(已排序
  5. 否则(未排序)我们需要更新缓冲区(增加它) - 这通过检查当前元素的差异和已经排序的数组的最大值来简单地完成element(所以我们将abs((curr-prev)/2) + 1添加到当前缓冲区)和之前/当前值(可能的最小值)。此时我们不需要关心先前的条目,因为我们正在增加缓冲区以减少先前/当前值,我们可以简单地从每个先前的值中减去abs((curr-prev)/2) + 1,并且不变量将保持不变。
  6. 一些例子让它更加清晰(粗体 - 当前,斜体 - 上一个):

      

    I)输入:[4,0,9,-2]

    • [ 4 ] - 按定义排序
    • [ 4 0 ] - 未排序,使用缓冲区[ 4 0 ]更新当前版本, diff =(4-0)/ 2 + 1 = 3 => [1,2],缓冲区= 3 //更新完成如下:将整个差异减小前一个值,对于当前的一个检查newPrevious(此处为1)加1是否在范围当前+ -diff中,如果是,则设置newPrevious + 1,否则设置current + diff
    • [1, 2 9 ] - 排序,使用缓冲区[1, 2 6 ] //这里的更新与之前的更新类似,但是关闭当前的+ diff我们做current-diff
    • [1,2, 6 -2 ] - 未排序,使用缓冲区更新当前的一个[1,2, 6 ,<强> 1 ],仍未分类,因此diff =(6-1)/ 2 + 1 = 3,缓冲区= 6,[1,2, 3 4

    完成,缓冲区= 6

      

    II)输入:[40,31,140,​​131]

    • [ 40 ] - 按定义排序
    • [ 40 31 ] - 未排序,使用缓冲区更新当前的[40,31],diff =(40-31)/ 2 + 1 = 5 = &GT; [35,36],缓冲区= 5
    • [35, 36 140 ] - 排序,更新当前的[35,36,135]
    • [35,36, 135 131 ] - 未分类,更新当前的[35,36,135,136]

    完成,缓冲区= 5

      

    III)输入:[1,1,1,1]

    • [ 1 ] - 按定义排序
    • [1, 1 ] - 未排序,更新当前的一个[1,1],diff =(1-1)/ 2 + 1 = 1 =&gt; [0,1](因为0 + 1没问题),缓冲区= 1
    • [0, 1 1 ] - 未排序,更新当前的[0,1,2],已排序
    • [0,1, 2 1 ] - 未排序,更新当前一个[0,1,2,2],diff =(2-2)/ 2 + 1 = 1 =&gt; [0,1,2,3],缓冲区= 2

    完成,缓冲区= 2

      

    IV)输入:[7,11,1,2,3]

    • [ 7 ] - 按定义排序
    • [ 7 11 ] - 排序,调整[7,11]
    • [7, 11 1 ] - 未排序,调整[7,11,1],diff =(11-1)/ 2 + 1 = 6 = &GT; [7,5,6],缓冲区= 6
      • 这里我们可以看到我们不需要在11之前检查任何东西,因为之前的所有数字都小于11,所以如果我们做11 - X我们可以简单地做PREVIOUS - X和不变量<仍然会举行
    • [7,5, 6 2 ] - 未分类,调整[7,5,6,7]&lt; - 不变仍然保留,我只是没有' t调整上一步中的前7个
    • [7,5,6, 7 3 ] - 未分类,调整[7,5,6,7,8]

    完成,缓冲区= 6

      

    V)输入:[0,1,3,-15]

    直到[0,1,3]没有变化

    • [0,1, 3 -15 ] diff = 10,再次调整prev和current [0,1,-7,-6] - 这里我们我们可以看到我们将缓冲区从0增加到10,因此3和3左边的所有数字都可以减少10并且不变量将保持不变,但对于算法的其余部分我们不需要这样做

    完成,缓冲区= 10

      

    VI)输入:[1,2,3,4]

    • [1] - 按definiton排序
    • [1,2] - 排序,按缓冲区0调整
    • [1,2,3] - 排序,按缓冲区0调整
    • [1,2,3,4] - 排序,按缓冲区0调整

    完成,缓冲区= 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

原始答案的基本原理和缺陷

我原来的答案背后的想法是maxmin之间的中点提供了target值,规范化数组中的每个条目都可以被调整为命中。距离max最远的mintarget需要最大的调整,因此提供了答案。但是,在看到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,找出maxmin之间的最大差异。最大的差异除以2并在必要时进行四舍五入是问题的答案。此外,minmax之间的中点是数组该部分的目标值(目标值可以被截断)。

示例

考虑数组[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)解决方案。

对于相邻的一对,例如ab,如果发生a > b,我们需要调整它们。要按升序调整它们,我们需要找到X a - X < b + X,这相当于找到满足X的{​​{1}}。因此,我们只需要扫描一次数组,找到a - b < 2X对,并计算a > b,并回答最大值。

有效的实现可以写成如下:

ceil((a - b) / 2)