如何对整数数组进行排序,同时保持相同的元素彼此分开?

时间:2015-01-24 14:50:00

标签: ruby arrays sorting

我不确定这种类型的名称是否存在,所以我很难在其他地方找到相关的答案。

我希望元素尽可能地旋转。

鉴于此数组:

[38,38,40,40,40,41,41,41,41,60]

如何将其排序?

[38,40,41,60,38,40,41,40,41,41]

我研究过其他主题,但未能找到答案。

提前致谢!

3 个答案:

答案 0 :(得分:3)

这本身并不是排序"排序"本身。这是一个排序步骤,但最终您真正尝试做的事情称为matrix transposition

以下两种实现方式。

更详细

第一种方式更长,为了清晰起见,分解为更多步骤(希望如此)。

首先,我们将相关的数组元素排序并分组在一起:

v = arr.sort.group_by { |e| e }.values
# => [[38, 38], [40, 40, 40], [41, 41, 41, 41], [60]]

让我们为结果创建一个新数组:

r = []

现在我们将获得具有最多元素的那个:

max = arr.map { |e| arr.count(e) }.max

然后我们多次遍历数组,

max.times { ... }

每次从每个子阵列中拉出一个元素,然后将其放到结果数组中:

max.times { v.each { |a| r << a.shift } }; r.compact!

我们有答案:

# => [38, 40, 41, 60, 38, 40, 41, 40, 41, 41]

更简洁

既然你已经看到了漫长的道路,那么这里的方法更为简洁。这并不需要尽可能多的迭代(或输出数组!)。

首先,我们将子数组填充为max

的大小
arr.sort.
  group_by { |e| e }.values.
  map { |a| a.fill(nil, a.size..max-1) }
# ...

现在这是maxmax元素的正方形数组,因此我们可以将其视为方阵。这意味着我们可以转置元素,以便&#34;行&#34;成为&#34;列&#34;,反之亦然。

因此,我们transpose,然后flatten获取单个数组,然后compact删除nil元素:

# ...
  (...).transpose.flatten.compact

我们得到了理想的结果:

# => [38, 40, 41, 60, 38, 40, 41, 40, 41, 41]

答案 1 :(得分:0)

这是另一种解决方法。看起来约翰已经给出了一个很好的答案,所以我不会详细解释这个问题。

要了解其工作原理,我建议添加一些puts语句来查看中间值。您可以使用watch ruby sorter_test.rb运行它以不断获得反馈。 (见watch docs)。

此处使用的大多数方法来自Enumerable module

require 'minitest/autorun'

class Sorter < Struct.new(:list)
  def call
    sorted_and_grouped = list.sort.group_by { |i| i }.values
    max_len = sorted_and_grouped.max_by(&:count).count
    padded = sorted_and_grouped.map { |xs| pad(xs, max_len) }
    padded.reduce(&:zip).flatten.compact
  end

  private

  def pad(xs, len)
    xs.fill(nil, xs.count...len)
  end
end

ORIGINAL  = [38, 38, 40, 40, 40, 41, 41, 41, 41, 60].freeze
SORTED    = [38, 40, 41, 60, 38, 40, 41, 40, 41, 41].freeze
class TestSorter < Minitest::Test
  def test_sorts_as_expected
    actual = Sorter.new(ORIGINAL).call
    assert_equal SORTED, actual
  end
end

答案 2 :(得分:0)

这是另一种方式。这取决于您的数组的排序(增加或减少),就像在您的示例中一样。而且,如果没有这种假设,问题就没有明确定义。

首先,我经常希望有一位帮手:

class Array
  def %(arr)
    arr.each_with_object(dup) do |e,a|
      i = a.index(e)  
      a.delete_at(i) if i
    end
  end
end

例如:

arr = [38, 38, 40, 40, 40, 41, 41, 41, 41, 60]
arr % [41, 60, 40, 38, 40, 41] 
  #=> [38, 40, 41, 41]

如果ab是两个数组,则a%ba-b类似,只是删除a中包含的所有b元素。 {1}},它为a中该角色的每个实例删除b中的一个字符(索引最小的一个字符)。现在这不是一个方便的内置方法吗?

使用这个帮助器,以所需的方式对数组进行排序是一件简单的事情:

  1. 复制a数组并创建一个空数组result
  2. u的唯一元素a添加到result
  3. 使用帮助程序从u删除a
  4. 如果a为空,请返回result;否则重复第2步。
  5. 此代码如下:

    def steves_sort(arr)
      a = arr.dup
      result = []
      while a.any?
        u = a.uniq
        result.concat(u)
        a = a % u
      end
      result
    end
    
    steves_sort([38, 38, 40, 40, 40, 41, 41, 41, 41, 60])
      #=> [38, 40, 41, 60, 38, 40, 41, 40, 41, 41]