Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/main/java/com/thealgorithms/others/IterativeFloodFill.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.thealgorithms.others;

import java.util.LinkedList;
import java.util.Queue;

public final class IterativeFloodFill {
private IterativeFloodFill() {
}

/**
* Represents a point in 2D space with integer coordinates.
*/
private static class Point {
final int x;
final int y;

Point(final int x, final int y) {
this.x = x;
this.y = y;
}
}

/**
* Checks if a pixel should be skipped during flood fill operation.
*
* @param image The image to get boundaries
* @param x The x co-ordinate of pixel to check
* @param y The y co-ordinate of pixel to check
* @param oldColor The old color which is to be replaced in the image
* @return {@code true} if pixel should be skipped, else {@code false}
*/
private static boolean shouldSkipPixel(final int[][] image, final int x, final int y, final int oldColor) {

if (x < 0 || x >= image.length || y < 0 || y >= image[0].length || image[x][y] != oldColor) {
return true;
}

return false;
}

/**
* Iteratively fill the 2D image with new color
*
* @param image The image to be filled
* @param x The x co-ordinate at which color is to be filled
* @param y The y co-ordinate at which color is to be filled
* @param newColor The new color which to be filled in the image
* @param oldColor The old color which is to be replaced in the image
* @see <a href=https://www.geeksforgeeks.org/dsa/flood-fill-algorithm>FloodFill BFS<a/>
*/
public static void floodFill(final int[][] image, final int x, final int y, final int newColor, final int oldColor) {
if (image.length == 0 || image[0].length == 0 || newColor == oldColor || shouldSkipPixel(image, x, y, oldColor)) {
return;
}

Queue<Point> queue = new LinkedList<>();
queue.add(new Point(x, y));

int[] dx = {0, 0, -1, 1, 1, -1, 1, -1};
int[] dy = {-1, 1, 0, 0, -1, 1, 1, -1};

while (!queue.isEmpty()) {
Point currPoint = queue.poll();

if (shouldSkipPixel(image, currPoint.x, currPoint.y, oldColor)) {
continue;
}

image[currPoint.x][currPoint.y] = newColor;

for (int i = 0; i < 8; i++) {
int curX = currPoint.x + dx[i];
int curY = currPoint.y + dy[i];

if (!shouldSkipPixel(image, curX, curY, oldColor)) {
queue.add(new Point(curX, curY));
}
}
}
}
}
163 changes: 163 additions & 0 deletions src/test/java/com/thealgorithms/others/IterativeFloodFillTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package com.thealgorithms.others;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

import org.junit.jupiter.api.Test;

class IterativeFloodFillTest {

@Test
void testForEmptyImage() {
int[][] image = {};
int[][] expected = {};

IterativeFloodFill.floodFill(image, 4, 5, 3, 2);
assertArrayEquals(expected, image);
}

@Test
void testForSingleElementImage() {
int[][] image = {{1}};
int[][] expected = {{3}};

IterativeFloodFill.floodFill(image, 0, 0, 3, 1);
assertArrayEquals(expected, image);
}

@Test
void testForEmptyRow() {
int[][] image = {{}};
int[][] expected = {{}};

IterativeFloodFill.floodFill(image, 4, 5, 3, 2);
assertArrayEquals(expected, image);
}

@Test
void testForImageOne() {
int[][] image = {
{0, 0, 0, 0, 0, 0, 0},
{0, 3, 3, 3, 3, 0, 0},
{0, 3, 1, 1, 5, 0, 0},
{0, 3, 1, 1, 5, 5, 3},
{0, 3, 5, 5, 1, 1, 3},
{0, 0, 0, 5, 1, 1, 3},
{0, 0, 0, 3, 3, 3, 3},
};

int[][] expected = {
{0, 0, 0, 0, 0, 0, 0},
{0, 3, 3, 3, 3, 0, 0},
{0, 3, 2, 2, 5, 0, 0},
{0, 3, 2, 2, 5, 5, 3},
{0, 3, 5, 5, 2, 2, 3},
{0, 0, 0, 5, 2, 2, 3},
{0, 0, 0, 3, 3, 3, 3},
};

IterativeFloodFill.floodFill(image, 2, 2, 2, 1);
assertArrayEquals(expected, image);
}

@Test
void testForImageTwo() {
int[][] image = {
{0, 0, 1, 1, 0, 0, 0},
{1, 1, 3, 3, 3, 0, 0},
{1, 3, 1, 1, 5, 0, 0},
{0, 3, 1, 1, 5, 5, 3},
{0, 3, 5, 5, 1, 1, 3},
{0, 0, 0, 5, 1, 1, 3},
{0, 0, 0, 1, 3, 1, 3},
};

int[][] expected = {
{0, 0, 2, 2, 0, 0, 0},
{2, 2, 3, 3, 3, 0, 0},
{2, 3, 2, 2, 5, 0, 0},
{0, 3, 2, 2, 5, 5, 3},
{0, 3, 5, 5, 2, 2, 3},
{0, 0, 0, 5, 2, 2, 3},
{0, 0, 0, 2, 3, 2, 3},
};

IterativeFloodFill.floodFill(image, 2, 2, 2, 1);
assertArrayEquals(expected, image);
}

@Test
void testForImageThree() {
int[][] image = {
{1, 1, 2, 3, 1, 1, 1},
{1, 0, 0, 1, 0, 0, 1},
{1, 1, 1, 0, 3, 1, 2},
};

int[][] expected = {
{4, 4, 2, 3, 4, 4, 4},
{4, 0, 0, 4, 0, 0, 4},
{4, 4, 4, 0, 3, 4, 2},
};

IterativeFloodFill.floodFill(image, 0, 1, 4, 1);
assertArrayEquals(expected, image);
}

@Test
void testForSameNewAndOldColor() {
int[][] image = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

int[][] expected = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

IterativeFloodFill.floodFill(image, 0, 1, 1, 1);
assertArrayEquals(expected, image);
}

@Test
void testForBigImage() {
int[][] image = new int[100][100];

assertDoesNotThrow(() -> IterativeFloodFill.floodFill(image, 0, 0, 1, 0));
}

@Test
void testForBelowZeroX() {
int[][] image = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

int[][] expected = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

IterativeFloodFill.floodFill(image, -1, 1, 1, 0);
assertArrayEquals(expected, image);
}

@Test
void testForBelowZeroY() {
int[][] image = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

int[][] expected = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

IterativeFloodFill.floodFill(image, 1, -1, 1, 0);
assertArrayEquals(expected, image);
}

@Test
void testForAboveBoundaryX() {
int[][] image = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

int[][] expected = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

IterativeFloodFill.floodFill(image, 100, 1, 1, 0);
assertArrayEquals(expected, image);
}

@Test
void testForAboveBoundaryY() {
int[][] image = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

int[][] expected = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}};

IterativeFloodFill.floodFill(image, 1, 100, 1, 0);
assertArrayEquals(expected, image);
}
}