使用简单数组通过堆叠数字对数字进行排序?

时间:2013-04-09 00:40:18

标签: java arrays algorithm

这是一个家庭作业问题(对不起,我知道这些都是不受欢迎的),但是我和老师都不知道如何有效地解决它,所以我想把它放在这里,看看SO上的精彩大脑是否可以帮助我们。

给出了一个未指定大小的数组,其中包含随机数。它必须按递增顺序排序。每个元素可以移动到相邻的空白空间,也可以移动到相邻的较大元素的顶部。我需要编写一个方法来返回给定数组排序所需的最小移动次数。

这个问题被标记为“可选”,因为老师意识到这个问题太难了,但我很好奇它是如何解决的。对任何大小的数组的任何建议(对于我所关心的所有数组都可以是长度为3的数组)。

编辑:感谢您指出这一点尚不清楚。我正在使用数组来表示假设的现实世界情况。让我们使用硬币的例子:它们全部放在一张桌子上,并且只有一定数量的“空间”可以放入。但是它们可以放在相邻的较大硬币之上,或者滑动到一个相邻的空地(已被一枚可能在一堆上移动的硬币腾空)。

3 个答案:

答案 0 :(得分:1)

我决定用一些假设/变化来检验这个问题,因为这对我来说是一个更有趣的问题:

1)您可以从堆的任何部分向左或向右移动元素。

2)无论是更大,更小还是相同的尺寸,您都可以将元素堆叠在一堆上。

3)只要你在一个较小的数字之前从未遇到过更大的数字,无论你如何通过堆栈,数组都被认为是有序的。所以_ 11 2 3已经排序,但是_ _ 12 3并不是因为你可以将2解释为在1之前。

这导致了一个非常有趣的问题,尽管有这个公理:

Axiom A:你做出动作的顺序是无关紧要的,并且可以以任何方式重新安排以达到相同的最终结果。

Axiom Ab:如果数组中没有重复,那么只需将每个元素移动到最终位置。

特别是,我制定了一个策略,希望你只用本地检查就可以解决它,没有递归/回溯,但我已经证明这是徒劳无功的,并会在以后显示出来。

我的策略是:

1)从左到右继续寻找以错误方式翻转的对(较低数字前的较高数字)。

2a)当你找到一个时,如果有一个空白点或一个右手值可以立即填充的堆栈,请向左移动直到它填满它。

2b)否则,将左侧值向右移动一个。这会产生一种情况,即你有一堆无关紧要的数字 - 首先,在向下比较之前,根据1的逻辑将你移动的值与新右边的值进行比较。

2bii)以与对比较相同的方式处理向下比较 - 如果它可以适合空点或堆栈,则向左移动较低值,否则向右移动较高值并继续。

示例:

1231 - >我们向左移动1b因为它适合堆叠。 11 2 3 _

1321 - >我们向右移3,因为2不适合空位/堆栈。我们向左移动1b两次,因为它将适合一个空点/适合堆栈,然后我们向右移动3,因为2不适合空位/堆栈。 1 1 2 3

1132 - >我们向右转3,因为2不能左转。我们向左移动2,因为它适合空位。 1 1 2 3

2311 - >我们向右移动3,因为1a不能向左移动。我们将1b左移两次因为它适合空位。我们将1a向左移动,因为它会叠加。我们向右移动2因为1a1b不能向左移动。我们向左移动1a2b,因为它将填补一个空位。 11 2 3 _

但是,我们遇到了这两个起始数组的问题:

23411 10移动最佳,2r 3r 4r 1al * 3 1bl * 4使11 2 3 4 _。

23111 9移动最佳,2r * 3 3r * 3 1bl 1cl * 2使_ _ 111​​ 2 3 - 与23411策略相反!我们减少了1s和23次,因为有更多的1,所以我们保存移动它们尽可能少。

这表明我们不能只进行简单的局部比较来解决这个新问题。

我仍然在考虑这个问题,以一种有趣的方式显得非常微不足道,我希望你们中的一些人喜欢和我一起思考这个问题:)

答案 1 :(得分:0)

我假设:

  • “已排序”表示剩下一个堆栈
  • 如果源堆栈中的最大数量小于目标堆栈中的最小数量,我们只能将堆栈移动到另一个堆栈(或者等效地:堆栈必须在顶部用较小的数字排序,并将堆栈移动到另一个堆栈上将整个堆栈移到另一个堆栈之上)

然后,如果数组包含多于一次的数字,则显然没有解决方案。此外,数字的大小无关紧要,只有它们的排名。因此,在不失一般性的情况下,我们可以假设数组包含任意顺序的数字1..n。此外,为了确定可能的移动,只有堆栈底部的顶部很重要。因此,存储足够了:

int[] bottom;
int[] top;

由于我们不能将堆栈分开,我们可能只将堆栈i移动到堆栈j上bottom[i] + 1 == top[j],否则我们将最终处于无法解决的状态。但是,如果我们处于可以进行此类移动的游戏状态,则最好立即执行此操作,因为最终我们必须组合这些堆栈,并且移动单个堆栈比移动两个堆栈更便宜。 / p>

因此,唯一的自由度是如何以最少的移动次数将堆栈移动到位。此时,我没有看到明显的贪婪算法,因此找到最佳解决方案可能需要将游戏的位置编码为图形,并将最短路径算法应用于该图形以找到最短的移动序列。

答案 2 :(得分:0)

编辑:鉴于每个人似乎都在解决不同的问题,我会陈述我正在解决的问题(我认为这是正确的问题(不是我们都是吗?) ):(使用磁盘有希望让事情更容易理解)

  

给定n个堆,每个堆只包含1个磁盘,按照大小的顺序排列这些磁盘。最终结果必须使每个堆包含1个磁盘。唯一允许的操作是将单个磁盘从一个堆的顶部移动到相邻堆的顶部,但受限于没有磁盘可以放置在较小的磁盘上。允许将磁盘移动到空堆或从堆中移动最后一个磁盘。总是有n堆,因此:(1)可能无法创建新桩,因此磁盘可能不会移动到原始序列的边界之外。 (2)空堆仍然存在,因此如果相邻位置为空,则在没有先移动到该位置的情况下,盘可能不会移动到该位置。

注意:

直径为x =数字x的磁盘。

这可能不是最有效的解决方案,但它应该起作用并且可能会让某人有所作为。

这是一个纯粹的迭代过程 - 在每一步之后,我们以所有大小为1的堆栈结束;这种方法可能是一种根本的低效率。

<强>解决方案:

我将使用1,2和5来说明以下内容,但这仅用于表示大小排序,对于具有相同排序的任何其他数字,它保持相同。

  • 重复直到排序:
    • 找出尚未位于正确位置的最大元素(在这种情况下为5
      • (实际上它不一定是最大的,只是以下形式的任何东西,但要注意不要将元素移动到它们应该的位置)
    • 我们有2个案例:
      • 位于最左侧位置,我们有2个案例:
        • 512... - 向右移动1,向右移动5,向左移动1 2,以152结束
        • 521... - 向左移动1 2,向右移动2,向右移动1 2,向右移动5,移动1 2向左,以152
        • 结尾
      • 它不在最左边的位置,我们有2个案例:
        • ...251... - 向左移动1 2,向右移动5,向右移动1,以215结束
        • ...152... - 向左移动2,向右移动1,向右移动1,向左移动2,以251结束(之后)我们从之前的案例中采取步骤)

编辑2:

更有效的解决方案:

  1. 找到不在正确位置的最小磁盘
  2. 将它放在右侧的磁盘上
  3. 将所有磁盘移至该磁盘1左侧的位置
  4. 将最小的磁盘一直向左移动(直到遇到一个已经位于正确位置的较小磁盘)。
  5. 可能的预处理步骤:排序到单独的列表以有效地查找最小元素

    优化:如果您要将磁盘向右移动超过其目标位置,请不要在上一步中将磁盘向右移动,而只是放置该磁盘上的这个磁盘。如何在合适的时间有效地做到这一点可能有点复杂。不执行某些措施以提高效率也可能有所帮助。

    示例:

    52413
    
    // put smallest disk (1) on disk to the right
        1
    524_3
    
    // shift disks to the left 1 position right (3 moves - moved 4, then 2, then 5)
        1
    _5243
    
    // move 1 all the way left (4 moves)
    15243
    
    // same as above for 2
    
       2
    15_43
    
       2
    1_543
    
    12543
    

    当最小的磁盘位于最右侧位置时(现在是3的情况),这有点问题。 2种解决方法:

    • 交换12并将1放在21 2个位置,2左,{{{ 1}}剩下2个位置)。那么你就有一个可以移动1的空位。如果少于2个较小的元素,则不是一个选项。在我们对相同的情况进行分类之前,我们不应该修复这些元素。

    • 如果我们有3,我们可以简单地将12453放在4上,为5打开一个插槽(这会延迟问题)。如果第一个未排序的磁盘(在这种情况下为3)大于第二个(5),我们可以使用一些策略,就像我之前针对上面的解决方案所描述的那样,移动元素来满足这个条件