有更优雅的方式吗?

时间:2012-04-15 16:24:53

标签: arrays algorithm data-structures binary-tree

给定一个正整数数组a我想输出整数数组b,这样b[i]是最接近a[i]的数字,小于a[i] },并且在{a[0], ... a[i-1]}中。如果此号码不存在,则为b[i] = -1

示例:

a =  2  1 7 5 7 9
b = -1 -1 2 2 5 7

b[0] = -1因为没有小于2的数字 b[1] = -1,因为{2}中没有小于1的数字 b[2] = 2,距离{2,1}小于7的最接近7的数字是2 b[3] = 2,距离{2,1,7}小于5的最接近5的数字是2 b[4] = 5,距离{2,1,7,5}小于7的最接近7的数字为5

我在考虑实现平衡二叉树,但是需要做很多工作。有没有更简单的方法呢?

4 个答案:

答案 0 :(得分:2)

这是一种方法:

 for i ← 1 to i ← (length(A)-1) {
     // A[i] is added in the sorted sequence A[0, .. i-1] save A[i] to make a hole at index j
     item = A[i]
     j = i

     // keep moving the hole to next smaller index until A[j - 1] is <= item
     while j > 0 and A[j - 1] > item {
         A[j] = A[j - 1]  // move hole to next smaller index
         j = j - 1
       }

     A[j] = item  // put item in the hole

     // if there are elements to the left of A[j] in sorted sequence A[0, .. i-1], then store it in b
     // TODO : run loop so that duplicate entries wont hamper results
     if j > 1
        b[i] = A[j-1]
     else 
        b[1] = -1;
   }

干运行:

a =  2 1 7 5 7 9
a[1] = 2

直截了当,将b[1]设为-1

a[2] = 1

插入子阵列:[1 ,2] 排序数组中1之前的任何元素?没有。 所以将b[2]设置为-1。 b: [-1, -1]

a[3] = 7

插入子阵列:[1 ,2, 7] 排序数组中7之前的任何元素?是。它的2 因此,将b[3]设置为2. b: [-1, -1, 2]

a[4] = 5

插入子阵列:[1 ,2, 5, 7] 排序数组中5之前的任何元素?是。它的2 因此,将b[4]设置为2. b: [-1, -1, 2, 2]

依旧......

答案 1 :(得分:1)

您可以将其视为插入排序。

伪代码:

let arr be one array with enough space for every item in a
let b be another array with, again, enough space for all elements in a
For each item in a:
    perform insertion sort on item into arr
    After performing the insertion, if there exists a number to the left, append that to b.
    Otherwise, append -1 to b
return b

你必须担心的主要事情是确保你不会犯错误重新分配数组(因为它会重新分配n次,这将是非常昂贵的)。这将是您使用的任何语言的实现细节(std :: vector为C ++保留... arr.reserve(n)用于D ... ArrayList的Java中的ensureCapacity ...)

与使用二叉树相比,这种方法的潜在垮台是它是O(n ^ 2)时间。但是,使用此方法与二叉树的常数因子会使较小的尺寸更快。如果你的n小于1000,这将是一个合适的解决方案。但是,O(n log n)比O(n ^ 2)增长慢得多,所以如果你期望一个大小明显更高,并且如果你有可能违反的时间限制,你可能会考虑更复杂的O( n log n)算法。

有一些方法可以略微提高性能(例如使用二进制插入排序:使用二进制搜索来查找要插入的位置),但通常它们在大多数情况下都不会提高性能,因为它仍然是O (n ^ 2)时间将元素移位以适应。

答案 2 :(得分:1)

这是一个(几乎)O(n log n)算法的草图,它介于实现插入排序和平衡二叉树的难度之间:向后解决问题,使用合并/快速排序,并使用二进制搜索。

伪代码:

let c be a copy of a
let b be an array sized the same as a
sort c using an O(n log n) algorithm
for i from a.length-1 to 1
    binary search over c for key a[i] // O(log n) time
    remove the item found // Could take O(n) time
    if there exists an item to the left of that position, b[i] = that item
    otherwise, b[i] = -1
b[0] = -1
return b

有一些实现细节可能会导致运行时间不佳。

  • 例如,由于你必须删除项目,在常规数组上执行此操作并转移周围将使此算法仍然需要O(n ^ 2)时间。因此,您可以存储键值对。一个是密钥,另一个是密钥的数量(类似于在阵列上实现的多集)。 “删除”只会从对中减去第二项,依此类推。

  • 最终你会留下一堆0值键。这最终会使if there exists an item to the left大约花费O(n)时间,因此,整个算法因此而降级为O(n ^ 2)。因此,另一个优化可能是定期批量删除所有这些。例如,当其中1/2为0值时,执行修剪。

  • 理想的选择可能是实现另一个具有更有利的删除时间的数据结构。一些带有索引的修改后的展开链表可能会有效,但肯定会增加这种方法的实现复杂性。

我实际上已经实现了这一点。我使用了上面的前两个优化(存储用于压缩的键值对,以及当其中1/2为0时修剪)。这里有一些使用插入排序衍生物进行比较的基准测试:

a.length  This method   Insert sort Method
100        0.0262ms      0.0204ms
1000       0.2300ms      0.8793ms
10000      2.7303ms     75.7155ms
100000    32.6601ms   7740.36  ms
300000    98.9956ms  69523.6   ms
1000000  333.501 ms     ????? Not patient enough

因此,正如您所看到的,此算法的增长速度远远超过我之前发布的插入排序方法。但是,插入排序方法需要73行代码和26行代码。因此,就简单性而言,如果您没有时间要求/输入很小,插入排序方法可能仍然是可行的方法。

答案 3 :(得分:0)

考虑一下:

a =  2  1 7 5 7 9
b = -1 -1 2 2 5 7

c   0 1 2 3 4 5 6 7 8 9
  0 - - - - - - - - - - 

其中C的索引是a [i]的值,使得0,3,4,6,8具有空值 并且C的第一维包含与[i]

最高的最接近的值
So in step by a[3] we have the following
    c    0  1  2  3  4  5  6  7  8  9
      0  - -1 -1  -  -  2  -  2  -  -

  and by step a[5] we have the following

    c    0  1  2  3  4  5  6  7  8  9
      0  - -1 -1  -  -  2  -  5  -  7

这样当我们到[4]的第二个7时,我们知道2是迄今为止最大的值,我们需要做的就是循环回[i-1]直到我们再次遇到7比较a [i]的值为c [7],如果更大,则替换c [7]。一旦[i-1] = 7,我们将c [7]放入b [i]并继续下一个[i]。

我可以看到这种方法的主要缺点是:

  • 足迹大小取决于c []需要确定的大小..
  • 你必须重新审视已经触及的[]的元素。如果数据的分布使得两个7之间存在显着的空间,那么随着时间的推移跟踪最高值可能会更快。或者,最好先在a [i]上收集统计数据,以了解存在哪些分布,然后使用维持最大值的混合方法,直到统计数据中不再有该数字的实例为止。