按成对差异排序数组

时间:2013-03-30 20:29:34

标签: algorithm sorting

例如我们有数组X[n] = {X0, X1, X2, ... Xn} 目标是对此数组进行排序,使每对之间的差异按升序排列。

例如X[] = {10, 2, 7, 4}

答案是:

2 7 10 4
4 10 7 2

我有一些代码,但这是蛮力:)

#include <stdio.h>

int main(int argc, char **argv)
{
    int array[] = { 10, 2, 7, 4 };
    int a[4];

    for(int i = 0; i < 4; i++){
        a[0] = array[i];

        for(int j = 0; j < 4; j++){
            a[1] = array[j];
            if(a[0] == a[1])
               continue;

            for(int k = 0; k < 4; k++){
                a[2] = array[k];
                if(a[0] == a[2] || a[1] == a[2])
                    continue;

            for(int l = 0; l < 4; l++){
                a[3] = array[l];
                if(a[0] == a[3] || a[1] == a[3] || a[2] == a[3])
                    continue;
                if(a[0] - a[1] < a[1] - a[2] && a[1] - a[2] < a[2] - a[3])  
                    printf("%d %d %d %d\n", a[0], a[1], a[2], a[3]);
             }
         }
     }
   }
    return 0;
 }

对“漂亮”算法的想法? :)

3 个答案:

答案 0 :(得分:2)

免责声明此解决方案将按差异值排列差异增长的项目。感谢@Will Ness

根据the difference between every pair is in ascending order要求的一种解决方案。

您只需按升序O(n)* log(n)对数组进行排序,然后从中间开始。你安排这样的元素:

[n/2, n/2+1, n/2-1, n/2+2, n/2-2, n/2+3 ...]如果更多元素位于第(n / 2)个元素的右侧,则首先转到+1

[n/2, n/2-1, n/2+1, n/2-2, n/2+2, n/2-3 ...]否则先去-1。

在这里,你可以获得成对的上升差异。

注意!!! 不能保证这个算法会找到最小的差异并从它开始,但我不认为这是要求。

实施例

排序数组:{1, 2, 10, 15, 40, 50, 60, 61, 100, 101}

然后,你选择50(10/2 = 5),60(10/2 + 1 = 6),40等等......

你会得到:{40, 50, 15, 60, 10, 61, 2, 100, 1, 101}

让你有所不同:10, 35, 45, 50, 51, 59, 88, 99, 100

答案 1 :(得分:2)

我们来看看。您的示例数组是{10,2,7,4},您显示的答案是:

2 7 10 4         
 5 3  -6     differences,  a[i+1] - a[i]

4 10 7 2
 6 -3 -5

我在这里展示了翻转的差异,这样分析更容易。

因此,目标是在降序顺序中存在差异a[i+1] - a[i]。显然,一些差异值将首先,然后一些。这意味着数组的最大元素将出现在中间的某个位置。到它的左边的正差必须在的绝对值,并且所述底片向右顺序 - 。在升的绝对值顺序

我们以另一个数组为例:{4,8,20,15,16,1,3}。我们从排序开始:

1 3 4 8 15 16 20
 2 1 4 7  1  4      differences,  a[i+1] - a[i]

现在,20位于中间,而在右侧之后必须将值逐渐分开。由于解决方案中20左边的差异是正的,因此值本身是升序的,即排序的。因此,在我们选择其中一些移动到最大元素的右侧之后剩下的任何东西,保持原样,并且(正)差异必须按降序排列。如果是,则找到解决方案。

这里没有解决方案。可能性是:

...  20 16 8    (no more)  left:  1 3 4 15    (diffs: 2 1 11 5) 
...  20 16 4    (no more)  left:  1 3 8 15    (diffs: 2 5 7 5)
...  20 16 3    (no more)  left:  1 4 8 15    (diffs: 3 4 7 5)
...  20 16 1    (no more)  left:  3 4 8 15  ....................
...  20 15 8    (no more)  left:  1 3 4 16
...  20 15 4    (no more)  left:  1 3 8 16
...  20 15 3    (no more)  left:  1 4 8 16
...  20 15 1    (no more)  left:  3 4 8 16
...  20 8       (no more)  left:  1 3 4 15 16
...  20 4       (no more)  left:  1 3 8 15 16
...  20 3       (no more)  left:  1 4 8 15 16
...  20 1       (no more)  left:  3 4 8 15 16
...  20         (no more)  left:  1 3 4 8  15 16

如果没有1和3,可能会有几种解决方案。

答案 2 :(得分:1)

并非总能解决此问题。例如,数组X[] = {0, 0, 0}无法根据需要进行“排序”,因为两个差异始终相等。

如果此问题有解决方案,则数组值应按“左图”所示进行“排序”:按升序排列的某些子集应构成结果数组的前缀,然后按降序排列所有剩余值形成后缀。 “排序”数组应该是凸的。

enter image description here

这给出了算法的提示:对数组进行排序,然后将其值拆分为两个凸子集,然后提取其中一个子集并在末尾附加(以相反的顺序)。

一个简单的(部分)实现是:对数组进行排序,找到属于convex hull的值的子集,然后检查所有剩余的值,如果它们是凸的,则在末尾附加它们。该算法仅在其中一个子集完全位于另一个子集之下时才有效。

如果生成的子集相交(如右图所示),则可以使用此算法的改进版本:将排序后的数组拆分为其中一个子集完全位于其他子集(AB,BC)之下的段,然后这些段中的每一个都找到凸包,并检查剩余子集的凸度。请注意,右图中的X轴以特殊方式对应于数组索引:对于子集交集(A,B,C),X对应于升序排序数组中的索引;交点之间的值的X坐标根据它们在结果数组中的位置进行缩放。

算法草图

  1. 按升序对数组进行排序。
  2. 从最大值开始,尝试将凸包值添加到“顶部”子集(以类似于Graham scan algorithm的方式)。同时将所有不属于凸包的值放到“底部”子集中并检查其凸度。继续,同时所有值都适合“顶部”或“底部”子集。处理最小值时,从数组中删除其中一个子集,反转子集,并附加到数组的和。
  3. 如果在向“top”子集添加一些值之后,“bottom”子集不再是凸起的,则回滚最后一次添加并检查该值是否可以正确地添加到“bottom”子集。如果没有,请停止,因为输入数组不能根据需要“排序”。否则,交换“顶部”和“底部”子集并继续执行步骤2(不应在子集之间移动已处理的值,任何移动它们的尝试都应导致转到步骤3)。
  4. 换句话说,我们可以处理排序数组的每个值,从最大到最小,尝试将此值附加到两个子集之一,使两个子集保持凸。首先,我们尝试将新值放置到添加了先前值的子集中。这可能会使之前添加的几个值不适合此子集 - 然后我们检查它们是否都适合其他子集。如果他们这样做 - 将它们移动到其他子集,如果不是 - 将它们保留在“顶部”子集中,但将当前值移动到其他子集。

    时间复杂度

    每个值最多从“顶部”子集添加或删除一次,也可以最多添加到“底部”子集一次。对于元素上的每个操作,我们只需要检查两个最接近的前辈。这意味着步骤2和3的最坏情况时间复杂度是O(N)。因此,总体时间复杂度由步骤1中的排序算法确定。