我在Python(版本3.2)中有一个2D数组,如下所示:
...AAA....
...AAABB..
..BBBBBCC.
.....CCCC.
.DDD..CC..
.DDD......
它代表一种区域绘制不同颜色的地图。上面的例子显示了四个不同的区域,A,B,C和D.
以下是索引数组的示例: map [1] [5] =='A'将返回True。
我正在尝试编写一个函数,它接受一个像这样的数组和一个row / col索引,并返回相同“颜色”的相邻空格的数量。因此,使用上面的示例,这里有一些返回值(参数分别是数组,行和列号:
6 <-- countArea(map, 5, 2)
8 <-- countArea(map, 2, 8)
我想将其作为递归函数实现,但我无法弄清楚。这是我到目前为止所做的:
def countArea(map, row, col):
key = map[row][col]
if (map[row-1][col] == key):
return 1 + countArea(map, row-1, col)
elif (map[row+1][col] == key):
return 1 + countArea(map, row+1, col)
elif (map[row][col+1] == key):
return 1 + countArea(map, row, col+1)
elif (map[row][col-1] == key):
return 1 + countArea(map, row, col-1)
else:
return 1
我知道我在这里缺少一些基本的东西。我基本上是说“这是当前的角色,现在看每个方向,看它是否有相同的角色。”
我的问题是,我在这个递归定义中缺少什么?
感谢您的帮助。
答案 0 :(得分:3)
我的问题是,我在这个递归定义中缺少什么?
一旦计算了网格方格,就不能再计算它(这包括通过countArea()
的递归调用进行计数!)
你当前的算法尽可能向北移动,然后向南迈出一步,然后向北迈出一步。这个两步序列重复,直到你的堆栈空间不足。
如果您愿意,可以在Wikipedia中阅读有关此问题的算法。
答案 1 :(得分:1)
在您的代码中,算法将查看给定输入字段左侧的一个字段,并且在递归调用中将再次调用初始字段上的函数。 (你显然不想要什么,因为它会导致无限递归)
方法1
在仍然使用递归时克服此问题的方法是指定方向,其中递归应查找相同类型的更多字段。例如,对初始场的直接向北(或高于)的字段调用可以递归地向北或向东(或向右)递增,向东看向向南(在下面)和向东等等。
通过智能选择第一步,您可以确保扫描区域中没有重叠。但是,它需要一些自适应来指定递归调用应扫描的方向。 但是请注意,如果该区域是悬垂的,则该算法将不起作用,因此如果不是通过向右和向上移动就可以到达起点东北的每个字段。
存在更多这样的算法,它们也能够解决上述问题。看看Flood Filling on wikipedia。
方法2
您还可以以某种方式保存已访问过的字段,如果已访问该字段,则直接从递归调用返回。
答案 2 :(得分:1)
以下实施应该有效:
def countArea(map, row, col, key=None, seen=None):
if key is None:
key = map[row][col]
if seen is None:
seen = set()
seen.add((row, col)) # mark this location as visited
n = 1
for dy, dx in [(0, 1), (1, 0), (-1, 0), (0, -1)]:
r, c = row + dy, col + dx
if r < 0 or r >= len(map) or c < 0 or c >= len(map[0]): # check boundaries
continue
# only increment and recurse if key matches and we haven't already visited
if map[r][c] == key and (r, c) not in seen:
n += countArea(map, r, c, key, seen)
return n
示例:
>>> print '\n'.join(''.join(row) for row in map)
...AAA....
...AAABB..
..BBBBBCC.
.....CCCC.
.DDD..CC..
.DDD......
>>> countArea(map, 5, 2)
6
>>> countArea(map, 2, 8)
8
请注意,这假设具有相同键的区域仅在对角线处接触时应视为单独的,例如对于以下地图countArea(map, 0, 0)
和countArea(map, 1, 1)
将返回1:
A.
.A
作为旁注,您不应将map
用作变量名称,因为它会掩盖内置map()
函数。