Skip to main content
Tweeted twitter.com/StackCodeReview/status/1021274891427905536
Added test case from https://codereview.stackexchange.com/a/200070/35991. The code is not modified, the answer is not invalidated.
Source Link
Martin R
  • 23.5k
  • 2
  • 36
  • 92

Swiftly countcounting rooms in a floor plan

7 9
#########
#.#.#.#.#
#.#.#...#
#.#...#.#
#.#.#.#.#
#.......#
#########
4 6
######
#.#..#
#....#
######
5 8
########
######.#
#......#
##.#...#
########
5 8
########
#..#...#
####.#.#
#..#...#
########
9 25
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#...#
#########################
3 3
...
...
...
3 3
###
...
###
3 3
###
###
###
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#...#.#
#.#####.#
#.......#
#########
5 8
########
#..#.#.#
##.#.#.#
#..#...#
########
7 8
########
#..#.#.#
##.#.#.#
#..#...#
########
#..#...#
########
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########
7 9
#########
#.#.##..#
#.#.##.##
#.#.##..#
#.#...#.#
#...#...#
#########
7 9
#########
#.#.....#
#.#.###.#
#.#...#.#
#.#####.#
#.......#
#########
7 9
#########
#.......#
#.#####.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########
1
1
1
3
47
1
1
0
2
2
4
1
1
1
1

Swiftly count rooms in a floor plan

4 6
######
#.#..#
#....#
######
5 8
########
######.#
#......#
##.#...#
########
5 8
########
#..#...#
####.#.#
#..#...#
########
9 25
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#...#
#########################
3 3
...
...
...
3 3
###
...
###
3 3
###
###
###
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#...#.#
#.#####.#
#.......#
#########
5 8
########
#..#.#.#
##.#.#.#
#..#...#
########
7 8
########
#..#.#.#
##.#.#.#
#..#...#
########
#..#...#
########
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########
7 9
#########
#.#.##..#
#.#.##.##
#.#.##..#
#.#...#.#
#...#...#
#########
7 9
#########
#.#.....#
#.#.###.#
#.#...#.#
#.#####.#
#.......#
#########
7 9
#########
#.......#
#.#####.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########
1
1
3
47
1
1
0
2
2
4
1
1
1
1

Swiftly counting rooms in a floor plan

7 9
#########
#.#.#.#.#
#.#.#...#
#.#...#.#
#.#.#.#.#
#.......#
#########
4 6
######
#.#..#
#....#
######
5 8
########
######.#
#......#
##.#...#
########
5 8
########
#..#...#
####.#.#
#..#...#
########
9 25
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#...#
#########################
3 3
...
...
...
3 3
###
...
###
3 3
###
###
###
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#...#.#
#.#####.#
#.......#
#########
5 8
########
#..#.#.#
##.#.#.#
#..#...#
########
7 8
########
#..#.#.#
##.#.#.#
#..#...#
########
#..#...#
########
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########
7 9
#########
#.#.##..#
#.#.##.##
#.#.##..#
#.#...#.#
#...#...#
#########
7 9
#########
#.#.....#
#.#.###.#
#.#...#.#
#.#####.#
#.......#
#########
7 9
#########
#.......#
#.#####.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########
1
1
1
3
47
1
1
0
2
2
4
1
1
1
1
Source Link
Martin R
  • 23.5k
  • 2
  • 36
  • 92

Swiftly count rooms in a floor plan

Inspired by recent questions about counting the rooms in a floor plan (1, 2, 3), here is my attempt to solve the problem with a Swift program.

The problem (from “Counting Rooms” on CSES) is:

You are given a map of a building, and your task is to count the number of rooms. The size of the map is \$ n \times m \$ squares, and each square is either floor or wall. You can walk left, right, up, and down through the floor squares.

##Input

The first input line has two integers \$n\$ and \$m\$: the height and width of the map.

Then there are \$n\$ lines of \$m\$ characters that describe the map. Each character is . (floor) or # (wall).

##Output

Print one integer: the number of rooms.

##Constraints

\$1\le n,m \le 2500\$

Example

###Input:

5 8
########
#..#...#
####.#.#
#..#...#
########

###Output:

3

I took this as an opportunity to learn about “disjoint-set data structures” (also called “union-find data structures”). Here is my implementation, it follows closely the pseudo-code from the Wikipedia page. The only difference is that the parent of the “representative” (or “root”) does not point to itself, but is nil.

UnionFind.swift

class UnionFind {
    
    var parent: UnionFind?
    var rank: Int
    
    init() {
        self.parent = nil
        self.rank = 0
    }
    
    // Find with path compression
    func find() -> UnionFind {
        if let parent = self.parent {
            let p = parent.find()
            self.parent = p
            return p
        }
        return self
    }
    
    // Combine the trees if the roots are distinct.
    // Returns `true` if the trees were combined, and
    // `false` if the trees already had the same root.
    @discardableResult func union(with other: UnionFind) -> Bool {
        var xRoot = self.find()
        var yRoot = other.find()
        
        if xRoot === yRoot {
            return false
        }
        
        if xRoot.rank < yRoot.rank {
            swap(&xRoot, &yRoot)
        }
        
        // Merge yRoot into xRoot
        yRoot.parent = xRoot
        if xRoot.rank == yRoot.rank {
            xRoot.rank += 1
        }
        return true
    }
}

The main code is highly inspired by Efficiently counting rooms from a floorplan (version 2):

  • The data is read from standard input, and can be more than one floor plan.
  • For each column we keep track of the room to which the field at the current row and this column belongs to. Here we use the UnionFind structure.
  • If a field is a continuation from both left and top then the two tracker values are combined with union().

main.swift

import Foundation

func readDimensions() -> (height: Int, width: Int)? {
    guard let line = readLine() else { return nil }
    let ints = line.split(separator: " ")
    guard ints.count == 2,
        let height = Int(ints[0]),
        let width = Int(ints[1]) else {
            return nil
    }
    return (height, width)
}

func readMapAndCountRooms(height: Int, width: Int) -> Int? {
    var tracker = Array<UnionFind?>(repeating: nil, count: width)
    var roomCount = 0
    
    for _ in 0..<height {
        guard let line = readLine(), line.count == width else { return nil }
        for (offset, field) in line.enumerated() {
            if field == "." {
                if let top = tracker[offset] {
                    // Continuation from the top ...
                    if offset > 0, let left = tracker[offset - 1] {
                        // ... and from the left:
                        if top.union(with: left) {
                            roomCount -= 1
                        }
                    }
                } else if offset > 0, let left = tracker[offset - 1] {
                    // Continuation from the left:
                    tracker[offset] = left
                } else {
                    // New room:
                    tracker[offset] = UnionFind()
                    roomCount += 1
                }
            } else {
                // A wall:
                tracker[offset] = nil
            }
        }
    }
    return roomCount
}

while let (height, width) = readDimensions(),
    let count = readMapAndCountRooms(height: height, width: width) {
        print(count)
}

For the input data

4 6
######
#.#..#
#....#
######
5 8
########
######.#
#......#
##.#...#
########
5 8
########
#..#...#
####.#.#
#..#...#
########
9 25
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#.#.#
#########################
#.#.#.#.#.#.#.#.#.#.#...#
#########################
3 3
...
...
...
3 3
###
...
###
3 3
###
###
###
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#...#.#
#.#####.#
#.......#
#########
5 8
########
#..#.#.#
##.#.#.#
#..#...#
########
7 8
########
#..#.#.#
##.#.#.#
#..#...#
########
#..#...#
########
7 9
#########
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########
7 9
#########
#.#.##..#
#.#.##.##
#.#.##..#
#.#...#.#
#...#...#
#########
7 9
#########
#.#.....#
#.#.###.#
#.#...#.#
#.#####.#
#.......#
#########
7 9
#########
#.......#
#.#####.#
#.#.#.#.#
#.#.#.#.#
#.......#
#########

this produces the (correct) output

1
1
3
47
1
1
0
2
2
4
1
1
1
1

Any feedback on the code is welcome, in particular:

  • This is my first experiment with union-find data structures. Is it correctly implemented? Can it be simplified?
  • Is the main program logic correct or can you spot any errors?