在Java中搜索二维char数组中的“气泡”

时间:2011-12-01 19:06:04

标签: java arrays

我正在处理Go Game项目中的问题。

我有一个电路板(goban),由2D数组字符表示。在每次下一步之前,我想检查数组中的“气泡”。气泡应该是由4个方向包围的另一组特定相同字符的相同字符的4连接区域。 如果存在这种“泡沫”,那么内部的字符应该被其他字符替换。但是可能会有更多的区域,而不是所有区域都被封闭。 例如,我有这个董事会:

      1  2  3  4  5  6  7  8  9  10 11 12 13
   -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
 A |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
 B |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
 C |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
 D |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
 E |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
 F |  +  +  O  O  O  O  +  +  +  +  +  +  +  | 
 G |  +  O  X  X  X  X  O  +  +  +  +  +  +  | 
 H |  +  +  O  O  X  X  O  +  +  +  +  +  +  | 
 I |  +  +  +  +  O  X  X  O  +  +  +  +  +  | 
 J |  +  +  +  +  O  X  O  +  +  +  +  +  +  | 
 K |  +  +  +  +  +  O  +  +  +  +  +  +  +  | 
 L |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
 M |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
   -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

我想找到Xs的气泡,计算它们并用'Z'替换它们。

我已经使用Google搜索,我认为某些连接组件标记算法或FloodFill可以完成这项工作,但我不确定如何实现它。这种方式或不那么复杂的东西可以解决它吗? 谢谢

编辑: 我试图找到一些可以找到具体特征区域并计算其自由度的模式,但是当位置是多层时它总是失败。 更改数据结构可能是解决方案,但如果可行,我希望现在就这样做。

我目前的解决方案想法:

public void solve(){
if (boardContainsEnclosedAreas(goban, onMovePlayerStone, oppositePlayerStone){
    onMovePlayerScore += countElementsInAreaAndReplaceThem(onMovePlayerStone, 'Z');  
}
}

public boolean boardContainsEnclosedAreas(char[][] playingBoard, char searchedChar, char surroundingChar){
// this method should find the bubble in the playingBoard array
}

public int countElementsInAreaAndReplaceThem(char searchedChar, char replacingChar){
// the method should go through the bubble and replace all inner chars 
// returns amount of replaced chars
}

2 个答案:

答案 0 :(得分:1)

您可以使用递归方法,确实使用 FloodFill 理论。

基本上,遍历您的网格,每次找到X时,将其替换为Z及其4个邻居(如果适用)。但诀窍是:不是每次都替换它们而不得不再次循环,而是再次调用相同的(调用)方法来执行它。递归将完成其余的工作。

这是(快速完成的)伪代码版本: (假设您的网格从0到xmax,从0到ymax索引)

int numberOfBubbles = 0;

for (x = 0 to xmax) {
    for (y = 0 to ymax) {
       if (getCharAt(x, y) == "X") { // this if is needed because you want to count the bubbles. If not, you could just do handleBubble(x, y);
           numberOfBubbles++;
           handleBubble(x, y);
       }
    }
}

// Recursive method
void handleBubble(int x, int y) {
    if (getCharAt(x, y) != "X") {
        return; // exit condition
    }

    getCharAt(x, y) = "Z";    
    if (x > 0) handleBubble(x-1, y);
    if (x < xmax) handleBubble(x+1, y);
    if (y > 0) handleBubble(x, y-1);
    if (y < ymax) handleBubble(x, y+1);
}

据我所知,对于这个问题,这是唯一的解决方案,无论你的泡泡是什么奇怪的形状都可以。复杂性也不错。

这可以进一步优化,因为它目前检查显然不再包含X的字符(因为它们刚被Z替换)。这留给读者一个练习:)

(注意:扫雷游戏,基于该解决方案)

答案 1 :(得分:0)

这是我用于类似图像分析需求的算法(伪代码):

regions = Collection<Set<Point>>
foreach (Point p : allBoardLocations)
  if (charAtLocation(p) != 'X') continue
  foundInRegion = false
  for (Set<Point> s : regions)
    if (s.contains(p))
      foundInRegion=true
      break;
  if (!foundInRegion)
    newRegion = new Set<Point>()
    stack = new Stack<Point>()
    stack.push(p)
    while (!stack.empty())
      foreach (Point n : neighboringPoints(stack.pop()))
        if (charAtLocation(n) == 'X')
          if (!newRegion.contains(n))
            newRegion.add(n);
            stack.push(n);

基本上,您维护一组点集合,其中每个集合代表板上连续点的“泡沫”。扫描板上的每个位置,如果它是'X'并且它不在某个区域中,那么创建一个新区域和一个包含该位置的堆栈,当堆栈中有任何项目时,访问其邻居搜索未访问的'X' ,将它们添加到新区域并堆叠为已发现。

最后,你将拥有一系列积分,每个积分代表一个“泡沫”。