Non-recursive implementation of Flood Fill algorithm?

强颜欢笑 提交于 2019-11-28 11:17:57
sukunrt

I'm assuming that you have some sort of a grid where you receive the coordinates of the location from where you would like to fill the area.

Recursive flood fill algorithm is DFS. You can do a BFS to convert it to nonrecursive.

Basically the idea is similar in both the algorithms. You have a bag in which the nodes that are yet to be seen are kept. You remove a node from the bag and put the valid neighbors of the node back into the bag. If the bag is a stack you get a DFS. If it's a queue you get a BFS.

the pseudocode is roughly this.

flood_fill(x,y, check_validity)
   //here check_validity is a function that given coordinates of the point tells you whether
   //the point should be colored or not
   Queue q
   q.push((x,y))
   while (q is not empty)
       (x1,y1) = q.pop()
       color(x1,y1)

       if (check_validity(x1+1,y1))
            q.push(x1+1,y1)
       if (check_validity(x1-1,y1))
            q.push(x1-1,y1)
       if (check_validity(x1,y1+1))
            q.push(x1,y1+1)
       if (check_validity(x1,y1-1))
            q.push(x1,y1-1)

NOTE: make sure that check_validity takes into account whether the point is already colored or not.


  • DFS: Depth First Search
  • BFS: Breadth First Search
Nikunj Banka

You basically have two ways to implement a flood fill algorithm non-recursively. The first method has been clearly explained by sukunrt in which you use a queue to implement breadth first search.

Alternatively, you can implement the recursive DFS non-recursively by using an implicit stack. For example, the following code implements a non-recursive DFS on a graph that has nodes as integers. In this code you use an array of Iterator to keep track of the processed neighbors in every node's adjacency list. The complete code can be accessed here.

public NonrecursiveDFS(Graph G, int s) {
        marked = new boolean[G.V()];

        // to be able to iterate over each adjacency list, keeping track of which
        // vertex in each adjacency list needs to be explored next
        Iterator<Integer>[] adj = (Iterator<Integer>[]) new Iterator[G.V()];
        for (int v = 0; v < G.V(); v++)
            adj[v] = G.adj(v).iterator();

        // depth-first search using an explicit stack
        Stack<Integer> stack = new Stack<Integer>();
        marked[s] = true;
        stack.push(s);
        while (!stack.isEmpty()) {
            int v = stack.peek();
            if (adj[v].hasNext()) {
                int w = adj[v].next();
                if (!marked[w]) {
                    // discovered vertex w for the first time
                    marked[w] = true;
                    // edgeTo[v] = w;
                    stack.push(w);
                }
            }
            else {
                // v's adjacency list is exhausted
                stack.pop();
            }
        }
    }

I am very good at flood fill algorythms, because I scanned all the available codes for them into a comparative sequence and I wrote a new version based on them.

So this information, That you will read, is the best possible implementation you can have on any programming platform.

All recursive and stack based implementations do not use organized memories or arrays to store the steps of pixels that they process.

Using an array declared explicitly and not relying on stack, you can rewrite all pixels your current known map of to scan and scanned pixels/voxels (if you are on 2d or 3d), at any line of your code and in any order withing the array.

The simplest and fastest version possible then becomes Recursion:

You recurse and write to THREE arrays copied as versions of the 2d/3d source array/image that you are writing from.

One for resulting flooded fill, One for source image, and one for "check this space next" ...

Your memory footprint is like 4 megs for a 1 meg source picture, using recursion.

Check this space next... is the magic array. You overwrite any extra recursions to the same check space and thereby unroll the recursion memory imprint by 1PowerN to being 1PowerX+Y.

I personally have achieved 2 gigapixels per second on a single core using that philosophy. it is the art of recursing through overwritable arrays. Simple. Figure it out and you will know the easiest, fastest, most simple to write, version possible writeable.

It's absurd to recurse through computer memory and create a new copy of every recursion result, when you are working on a source file with 1 million pixels or so. declare everything you need to write and read from withing the recursion as an array of the mapped XY stuff you are reading and writing, it just simplifies to three arrays sytaxed and a basic recursion.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!