I have tried to implement a solution to the Rainfall challenge based on the suggestions from 200_success♦ (Rainfall challenge).
Problem Statement
A group of farmers has some elevation data, and we're going to help them understand how rainfall flows over their farmland.
We'll represent the land as a two-dimensional array of altitudes and use the following model, based on the idea that water flows downhill:
If a cell’s four neighboring cells all have higher altitudes, we call this cell a sink; water collects in sinks.
Otherwise, water will flow to the neighboring cell with the lowest altitude. If a cell is not a sink, you may assume it has a unique lowest neighbor and that this neighbor will be lower than the cell.
Cells that drain into the same sink – directly or indirectly – are said to be part of the same basin.
Your challenge is to partition the map into basins. In particular, given a map of elevations, your code should partition the map into basins and output the sizes of the basins, in descending order.
Assume the elevation maps are square. Input will begin with a line with one integer, S, the height (and width) of the map. The next S lines will each contain a row of the map, each with S integers – the elevations of the S cells in the row. Some farmers have small land plots such as the examples below, while some have larger plots. However, in no case will a farmer have a plot of land larger than S = 5000.
Your code should output a space-separated list of the basin sizes, in descending order. (Trailing spaces are ignored.)
A few examples are below:
----------------------------------------- Input: Output: 3 7 2 1 5 2 2 4 7 3 6 9 The basins, labeled with A’s and B’s, are: A A B A A B A A A ----------------------------------------- Input: Output: 1 1 10 There is only one basin in this case. The basin, labeled with A’s is: A ----------------------------------------- Input: Output: 5 11 7 7 1 0 2 5 8 2 3 4 7 9 3 5 7 8 9 1 2 5 4 3 3 3 5 2 1 The basins, labeled with A’s, B’s, and C’s, are: A A A A A A A A A A B B A C C B B B C C B B C C C ----------------------------------------- Input: Output: 4 7 5 4 0 2 1 3 2 1 0 4 3 3 3 3 5 5 2 1 The basins, labeled with A’s, B’s, and C’s, are: A A B B A B B B A B B C A C C C -----------------------------------------
The code is in java. It would be great if anyone can review.
class Topography {
int[][] elevationData;
Cell[][] map;
List<Basin> basinList;
Topography(int[][] elevationData) {
this.elevationData = elevationData;
this.basinList = new ArrayList<Basin>();
this.map = new Cell[elevationData.length][elevationData.length];
}
private class Cell {
int altitude;
Basin basin;
Cell(int altitude) {
this.altitude = altitude;
this.basin = new Basin(this);
}
}
private class Basin {
Cell sinkCell;
HashSet<Cell> memberCells;
Basin(Cell sinkCell) {
this.sinkCell = sinkCell;
this.memberCells = new HashSet<Cell>();
this.memberCells.add(sinkCell);
}
}
private class BasinComparator implements Comparator<Basin> {
public int compare(Basin basin1, Basin basin2) {
if( basin1.memberCells.size() < basin2.memberCells.size() )
return 1;
else if( basin1.memberCells.size() > basin2.memberCells.size() )
return -1;
else
return 0;
}
}
private void createTopography() {
Cell tmpCell;
for(int i=0; i<elevationData.length; i++) {
for(int j=0; j<elevationData.length; j++) {
tmpCell = new Cell(elevationData[i][j]);
map[i][j] = tmpCell;
basinList.add(tmpCell.basin);
}
}
}
private Cell findSink(Cell cell) {
if(cell == null)
return null;
if(cell != cell.basin.sinkCell) {
cell.basin.sinkCell = findSink(cell.basin.sinkCell);
}
return cell;
}
private void union(Cell cellX, Cell cellY) {
Cell sinkX = findSink(cellX);
Cell sinkY = findSink(cellY);
if(sinkX == null || sinkY == null || sinkX == sinkY)
return;
if(sinkX.altitude > sinkY.altitude) {
sinkY.basin.memberCells.addAll(sinkX.basin.memberCells);
basinList.remove(sinkX.basin);
sinkX.basin = sinkY.basin;
} else {
sinkX.basin.memberCells.addAll(sinkY.basin.memberCells);
basinList.remove(sinkY.basin);
sinkY.basin = sinkX.basin;
}
}
void printBasinLength() {
createTopography();
for(int i=0; i<map.length; i++) {
for(int j=0; j<map.length; j++) {
Cell current_cell = map[i][j];
Cell minNeighbor = findMinimumNeighbor(i, j, current_cell);
if(minNeighbor != current_cell) {
union(minNeighbor, current_cell);
}
}
}
Collections.sort(basinList, new BasinComparator());
for(Basin basin: basinList)
System.out.print(basin.memberCells.size()+ " ");
}
private Cell findMinimumNeighbor(int i, int j, Cell current_cell) {
Cell min = current_cell;
if(i>0){
if(map[i-1][j].altitude < min.altitude)
min = map[i-1][j];
}
if(i<map.length-1){
if(map[i+1][j].altitude < min.altitude)
min = map[i+1][j];
}
if(j>0) {
if(map[i][j-1].altitude < min.altitude)
min = map[i][j-1];
}
if(j<map.length-1){
if(map[i][j+1].altitude < min.altitude)
min = map[i][j+1];
}
return min;
}
}