泛洪算法 Flood Fill
简介
泛洪算法——Flood Fill,(也称为种子填充——Seed Fill)是一种算法,用于确定连接到多维数组中给定节点的区域。 它被用在油漆程序的“桶”填充工具中,用于填充具有不同颜色的连接的,颜色相似的区域,并且在诸如围棋(Go)和扫雷(Minesweeper)之类的游戏中用于确定哪些块被清除。泛洪算法的基本原理就是从一个像素点出发,以此向周边相同或相似的像素点扩充着色,直到周边无相同颜色的区块或到图像边界为止。
泛洪填充算法采用三个参数:起始节点(start node),目标颜色(target color)和替换颜色(replacement color)。 该算法查找阵列中通过目标颜色的路径连接到起始节点的所有节点,并将它们更改为替换颜色。 可以通过多种方式构建泛洪填充算法,但它们都明确地或隐式地使用队列或堆栈数据结构。
四邻域泛洪
四邻域泛洪即向种子点上下左右四个方向传播。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void floodFill4Stack(int x, int y, int newColor, int oldColor) { if(newColor == oldColor) return; emptyStack(); static const int dx[4] = {0, 1, 0, -1}; static const int dy[4] = {-1, 0, 1, 0}; if(!push(x, y)) return; while(pop(x, y)) { screenBuffer[y][x] = newColor; for(int i = 0; i < 4; i++) { int nx = x + dx[i]; int ny = y + dy[i]; if(nx > 0 && nx < w && ny > 0 && ny < h && screenBuffer[ny][nx] == oldColor) { if(!push(nx, ny)) return; } } } }
|
八邻域泛洪
八邻域算法是将一个像素点的上下左右,左上,左下,右上,右下都进行着色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void floodFill8Stack(int x, int y, int newColor, int oldColor) { if(newColor == oldColor) return; emptyStack(); static const int dx[8] = {0, 1, 1, 1, 0, -1, -1, -1}; static const int dy[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; if(!push(x, y)) return; while(pop(x, y)) { screenBuffer[y][x] = newColor; for(int i = 0; i < 8; i++) { int nx = x + dx[i]; int ny = y + dy[i]; if(nx > 0 && nx < w && ny > 0 && ny < h && screenBuffer[ny][nx] == oldColor) { if(!push(nx, ny)) return; } } } }
|
描绘线算法(Scanline Fill)
利用填充线来加速算法, 它不是在堆栈上推动每个潜在的未来像素坐标,而是检查相邻线(前一个和下一个)以找到可能在未来通过中填充的相邻段, 线段的坐标(开始或结束)被推到堆栈上。 在大多数情况下,该扫描线算法至少比每像素算法快一个数量级(减少了大量的出栈入栈操作)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| void floodFillScanline(int x, int y, int newColor, int oldColor){ if(newColor==oldColor) return; if(screen[x][y]!=oldColor) return; emptyStack(); int x1; bool spanAbove, spanBelow; if(!push(x,y)) return; while(pop(x,y)){ x1=x; while(x1>0&&screen[x1][y]==oldColor) x1--; x1++; spanAbove = spanBelow = 0; while(x1<w&&screen[x1][y]==oldColor){ screen[x1][y]=newColor; if(!spanAbove&&y>0&&screen[x1][y-1]==oldColor){ if(!push(x1,y-1)) return; spanAbove=1; } else if(spanAbove&&y>0&&screen[x1][y-1]!=oldColor){ spanAbove=1; } if(!spanBelow&&y<h-1&&screen[x1][y+1]==oldColor){ if(!push(x1,y+1)) return; spanBelow=1; } else if(spanBelow&&y<h-1&&screen[x1][y+1]!=oldColor){ spanBelow=1; } x1++; } } }
|
案例
leet-code 733. 图像渲染这个题目描述有些抽象,不过本质是一个Flood Fill算法,leet-code 463. 岛屿的周长这个题不偷懒的话,需要一个标准的图像分割算法去解,发现多个种子点,种子点按边界生长。这里暂不过多介绍相见【TODO】
参考 & 引用
https://www.pianshen.com/article/172962944/