繁体   English   中英

在Java的2D字符数组中搜索“气泡”

[英]Searching for 'bubbles' in 2D array of chars in Java

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

我有一个棋盘(goban),以2D字符数组表示。 在进行下一步操作之前,我想检查数组中的“气泡”。 Bubble应该是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 |  +  +  +  +  +  +  +  +  +  +  +  +  +  | 
   -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

我想找到X的气泡,将其计数并用'Z'代替。

我已经在Google上搜索了,我认为某些Connected-component标签算法或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
}

您可以使用递归方法(实际上是使用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取代)。 这留给读者练习:)

(注意: 扫雷游戏以及其他基于该解决方案的游戏)

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

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” ,将它们添加到新区域并按发现的顺序堆叠。

最后,您将拥有一组点集,每个点集代表一个“气泡”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM