我正在努力解决以下问题:
给定一个矩形矩阵,它只有两个可能的值'X'和'O'。 值'X'总是以矩形岛和这些岛的形式出现 总是按行和逐列分隔至少一行“O”。 计算给定矩阵中的岛数。
我已经生成了一个解决方案但是我的循环没有经过整个矩阵。我已经尝试在i循环中将j初始化为0,但是会引发错误。我是这个主题的新手,我真的想要了解为什么我的代码不起作用。
def matrix(input)
row = input.length
col = input[0].length
counter = 0
i = 0
j = 0
while i < row do
while j < col do
if input[i][j] == "X"
if i == 0 || input[i-1][j] == "O" && j = 0 || input[i][j-1] == "O"
counter += 1
end
end
j += 1
end
i += 1
end
p counter
end
matrix(
[
["X", "X", "O", "X"],
["X", "X", "O", "X"],
["O", "O", "X", "O"],
["O", "O", "X", "O"],
["O", "O", "O", "X"],
["O", "O", "O", "X"]
]
)
# expected output is 4
这不是家庭作业。我正在练习数据结构和算法。可以找到原始问题here
答案 0 :(得分:2)
<强>算法强>
我们给出了一个数组input
的等大小数组,称为&#34; rows&#34;。问题中给出的例子如下。
input = [
["X", "X", "O", "X"],
["X", "X", "O", "X"],
["O", "O", "X", "O"],
["O", "O", "X", "O"],
["O", "O", "O", "X"],
["O", "O", "O", "X"]
]
为方便起见,我将元素input[3][2] #=> "X"
称为行 3
和列 2
中的元素(甚至虽然Ruby没有二维数组或具有行和列的数组的概念。)
第一步是构造一个数组groups
,每个元素(一个&#34; group&#34;)由一个元素组成,&#34; X&#34;的索引。在其中一行中:
groups
#=> [[[0, 0]], [[0, 1]], [[0, 3]], [[1, 0]], [[1, 1]], [[1, 3]],
# [[2, 2]], [[3, 2]], [[4, 3]], [[5, 3]]]
我们现在考虑groups
([[5, 3]]
)的最后一个元素,并询问它的任何元素(只有一个,[5, 3]
)是否与其中的元素位于同一个岛中任何其他团体。我们发现它与组[4, 3]
中的[[4, 3]]
位于同一个岛上(因为元素位于同一列中,相隔一行)。因此,我们删除最后一个组并将其所有元素(此处只是一个)添加到组[[4, 3]]
。我们现在有:
groups
#=> [[[0, 0]], [[0, 1]], [[0, 3]], [[1, 0]], [[1, 1]], [[1, 3]],
# [[2, 2]], [[3, 2]], [[4, 3], [5, 3]]]
我们现在使用现在的最后一个组[[4, 3], [5, 3]]
重复该过程。我们必须确定该组中的任何一个元素是否与每个其他组中的任何元素位于同一个岛上。它们不是 1 。因此,我们确定了第一个岛屿,包括地点[4, 3]
和[5, 3]
。
初始化islands = []
后,我们执行以下操作:
islands << groups.pop
所以现在
groups
#=>#=> [[[0, 0]], [[0, 1]], [[0, 3]], [[1, 0]], [[1, 1]], [[1, 3]],
# [[2, 2]], [[3, 2]]]
islands
#=> [[[4, 3], [5, 3]]]
我们继续这种方式,直到groups
为空,此时islands
的每个元素都是一个孤岛。
<强>代码强>
def find_islands(input)
groups = input.each_with_index.with_object([]) { |(row,i),groups|
row.each_with_index { |c,j| groups << [[i,j]] if c == 'X' } }
islands = []
while groups.any?
last_group = groups.pop
idx = groups.each_index.find { |idx| same_island?(groups[idx], last_group) }
if idx.nil?
islands << last_group
else
groups[idx] += last_group
end
end
islands.map(&:sort)
end
def same_island?(group1, group2)
group1.product(group2).any? { |(i1,j1),(i2,j2)|
((i1==i2) && (j1-j2).abs == 1) || ((j1==j2) && (i1-i2).abs == 1) }
end
示例强>
对于上面给出的数组input
,我们获得了以下岛屿数组。
find_islands(input)
#=> [[[4, 3], [5, 3]],
# [[2, 2], [3, 2]],
# [[0, 3], [1, 3]],
# [[0, 0], [0, 1], [1, 0], [1, 1]]]
<强>解释强>
对于没有经验的Rubiest来说,要学会理解我所提出的两种方法的运作方式,这是非常有用的。使用以下方法需要熟悉:
each_with_index
,any?
,find
,map
,product
each_index
,with_object
,next
pop
,sort
abs
nil?
(记录在Object) 2 groups
如果这是一个单独的方法(不是一个坏主意),我们会写下以下内容。
def construct_initial_groups(input)
input.each_with_index.with_object([]) { |(row,i),groups|
row.each_with_index { |c,`j| groups << [[i,j]] if c == 'X' } }
end
Ruby的新手可能会发现这有点压倒性。实际上,只是采用Ruby方式来收紧以下方法。
def construct_initial_groups(input)
groups = []
i = 0
input.each do |row|
j = 0
row.each do |c|
groups << [[i,j]] if c == 'X'
j += 1
end
i += 1
end
groups
end
从这里到达那里的第一步是使用方法Enumerable#each_with_index
。
def construct_initial_groups(input)
groups = []
input.each_with_index do |row,i|
row.each_with_index do |c,j|
groups << [[i,j]] if c == 'X'
end
end
groups
end
接下来,我们使用Enumerator#with_object
方法获取construct_initial_groups
上方的第一个表单。
编写块变量(|(row,i),groups|
)可能仍然令人困惑。你会了解到
enum = input.each_with_index.with_object([])
#=> #<Enumerator: #<Enumerator: [["X", "X", "O", "X"], ["X", "X", "O", "X"],
# ["O", "O", "X", "O"], ["O", "O", "X", "O"], ["O", "O", "O", "X"],
# ["O", "O", "O", "X"]]:each_with_index>:with_object([])>
是枚举器,其元素是通过应用方法Enumerator#next
生成的。
(row,i), groups = enum.next
#=> [[["X", "X", "O", "X"], 0], []]
Ruby使用消歧或分解为三个块变量中的每一个赋值:
row
#=> ["X", "X", "O", "X"]
i #=> 0
groups
#=> []
然后我们执行块计算。
row.each_with_index { |c,j| groups << [[i,j]] if c == 'X' }
#=> ["X", "X", "O", "X"].each_with_index { |c,j| groups << [[i,j]] if c == 'X' }
#=> ["X", "X", "O", "X"]
所以现在
groups
#=> [[[1, 0]], [[1, 1]], [[1, 3]]]
现在生成enum
的第二个元素并将其传递给块并执行块计算。
(row,i), groups = enum.next
#=> [[["O", "O", "X", "O"], 2], [[[1, 0]], [[1, 1]], [[1, 3]]]]
row
#=> ["O", "O", "X", "O"]
i #=> 2
groups
#=> [[[1, 0]], [[1, 1]], [[1, 3]]]
row.each_with_index { |c,j| groups << [[i,j]] if c == 'X' }
#=> ["O", "O", "X", "O"]
现在groups
有两个元素。
groups
#=> [[[1, 0]], [[1, 1]],
# [[1, 3]], [[2, 2]]]
其余的计算方法类似。
same_island? method
假设
group1 #=> [[0, 0], [1, 0]]
group2 #=> [[0, 1], [1, 1]]
这告诉我们[0, 0]
,[1, 0]
位于同一个岛上,[0, 1]
和[1, 1]
位于同一个岛上,但我们还不知道是否所有岛屿四个坐标在同一个岛上。为了确定是否是这种情况,我们将查看所有坐标对,其中一个来自group1
,另一个来自group2
。如果我们比较[0, 0]
和[1, 1]
,我们无法断定它们位于同一个岛屿(或不同的岛屿)上。但是,当我们比较[0, 0]
和[0, 1]
时,我们看到它们位于同一个岛上(因为它们位于相邻列中的同一行),因此我们推断两个组中的所有元素都在同一个岛屿。例如,我们可以将坐标从group2
移动到group1
,并从进一步的考虑中消除group2
。
现在考虑方法same_island?
为这两个群体执行的步骤。
group1 = [[0, 0], [1, 0]]
group2 = [[0, 1], [1, 1]]
a = group1.product(group2)
#=> [[[0, 0], [0, 1]], [[0, 0], [1, 1]], [[1, 0], [0, 1]],
# [[1, 0], [1, 1]]]
(i1,j1),(i2,j2) = a.first
#=> [[0, 0], [0, 1]]
i1 #=> 0
j1 #=> 0
i2 #=> 0
j2 #=> 1
b = i1==i2 && (j1-j2).abs == 1
#=> true
c = j1==j2 && (i1-i2).abs == 1
#=> false
b || c
#=> true
已经发现第一对坐标位于同一个岛上。 (事实上,由于发现c
为b
,因此无法执行true
的计算。)
find_islands
发表了评论
为了展示所有内容如何组合在一起,我将为方法find_islands
添加注释。
def find_islands(input)
groups = input.each_with_index.with_object([]) { |(row,i),groups|
row.each_with_index { |c,j| groups << [[i,j]] if c == 'X' } }
islands = []
# the following is the same as: until groups.empty?
while groups.any?
# pop removes and returns last element of `groups`. `groups` is modified
last_group = groups.pop
# iterate over indices of `groups` looking for one whose members
# are on the same island as last_group
idx = groups.each_index.find { |idx| same_island?(groups[idx], last_group) }
if idx.nil?
# last_group is an island, so append it to islands
islands << last_group
else
# groups[idx] members are found to be on the same island as last_group,
# so add last_group to group groups[idx]
groups[idx] += last_group
end
end
# sort coordinates in each island, to improve presentation
islands.map(&:sort)
end
1也就是说,没有元素groups[i,j]
,[4, 3]
或[5, 3]
位于同一列中且一行相隔一行或位于同一行中一列开