|
| 1 | +--- |
| 2 | +title: Finding faces of a planar graph |
| 3 | +tags: |
| 4 | + - Translated |
| 5 | +--- |
| 6 | +# Finding faces of a planar graph |
| 7 | + |
| 8 | +Consider a graph $G$ with $n$ vertices and $m$ edges, which can be drawn on a plane in such a way that two edges intersect only at a common vertex (if it exists). |
| 9 | +Such graphs are called **planar**. Now suppose that we are given a planar graph together with its straight-line drawing, which means that for each vertex $v$ we have a corresponding point $(x, y)$ and all edges are drawn as line segments between these points without intersection (such drawing always exists). These line segments split the plane into several regions, which are called faces. Exactly one of the faces is unbounded. This face is called **outer**, while the other faces are called **inner**. |
| 10 | + |
| 11 | +In this article we will deal with finding both inner and outer faces of a planar graph. We will assume that the graph is connected. |
| 12 | + |
| 13 | +## Some facts about planar graphs |
| 14 | + |
| 15 | +In this section we present several facts about planar graphs without proof. Readers who are interested in proofs should refer to [Graph Theory by R. Diestel](https://sites.math.washington.edu/~billey/classes/562.winter.2018/articles/GraphTheory.pdf) or some other book. |
| 16 | + |
| 17 | +### Euler's theorem |
| 18 | +Euler's theorem states that any correct drawing of a connected planar graph with $n$ vertices, $m$ edges and $f$ faces satisfies: |
| 19 | + |
| 20 | +$$n - m + f = 2$$ |
| 21 | + |
| 22 | +And more generally, every planar graph with $k$ connected components satisfies: |
| 23 | + |
| 24 | +$$n - m + f = 1 + k$$ |
| 25 | + |
| 26 | +### Number of edges of a planar graph. |
| 27 | +If $n \ge 3$ then the maximum number of edges of a planar graph with $n$ vertices is $3n - 6$. This number is achieved by any connected planar graph where each face is bounded by a triangle. In terms of complexity this fact means that $m = O(n)$ for any planar graph. |
| 28 | + |
| 29 | +### Number of faces of a planar graph. |
| 30 | +As a direct consequence of the above fact, if $n \ge 3$ then the maximum number of faces of a planar graph with $n$ vertices is $2n - 4$. |
| 31 | + |
| 32 | +### Minimum vertex degree in a planar graph. |
| 33 | +Every planar graph has a vertex of degree 5 or less. |
| 34 | + |
| 35 | +## The algorithm |
| 36 | + |
| 37 | +Firstly, sort the adjacent edges for each vertex by polar angle. |
| 38 | +Now let's traverse the graph in the following way. Suppose that we entered vertex $u$ through the edge $(v, u)$ and $(u, w)$ is the next edge after $(v, u)$ in the sorted adjacency list of $u$. Then the next vertex will be $w$. It turns out that if we start this traversal at some edge $(v, u)$, we will traverse exactly one of the faces adjacent to $(v, u)$, the exact face depending on whether our first step is from $u$ to $v$ or from $v$ to $u$. |
| 39 | + |
| 40 | +Now the algorithm is quite obvious. We must iterate over all edges of the graph and start the traversal for each edge that wasn't visited by one of the previous traversals. This way we will find each face exactly once, and each edge will be traversed twice (once in each direction). |
| 41 | + |
| 42 | +### Finding the next edge |
| 43 | +During the traversal we have to find the next edge in counter-clockwise order. The most obvious way to find the next edge is binary search by angle. However, given the counter-clockwise order of adjacent edges for each vertex, we can precompute the next edges and store them in a hash table. If the edges are already sorted by angle, the complexity of finding all faces in this case becomes linear. |
| 44 | + |
| 45 | +### Finding the outer face |
| 46 | +It's not hard to see that the algorithm traverses each inner face in a clockwise order and the outer face in the counter-clockwise order, so the outer face can be found by checking the order of each face. |
| 47 | + |
| 48 | +### Complexity |
| 49 | +It's quite clear that the complexity of the algorithm is $O(m \log m)$ because of sorting, and since $m = O(n)$, it's actually $O(n \log n)$. As mentioned before, without sorting the complexity becomes $O(n)$. |
| 50 | + |
| 51 | +## What if the graph isn't connected? |
| 52 | + |
| 53 | +At the first glance it may seem that finding faces of a disconnected graph is not much harder because we can run the same algorithm for each connected component. However, the components may be drawn in a nested way, forming **holes** (see the image below). In this case the inner face of some component becomes the outer face of some other components and has a complex disconnected border. Dealing with such cases is quite hard, one possible approach is to identify nested components with [point location](point-location.md) algorithms. |
| 54 | + |
| 55 | +<center></center> |
| 56 | + |
| 57 | +## Implementation |
| 58 | +The following implementation returns a vector of vertices for each face, outer face goes first. |
| 59 | +Inner faces are returned in counter-clockwise orders and the outer face is returned in clockwise order. |
| 60 | + |
| 61 | +For simplicity we find the next edge by doing binary search by angle. |
| 62 | +```{.cpp file=planar} |
| 63 | +struct Point { |
| 64 | + int64_t x, y; |
| 65 | + |
| 66 | + Point(int64_t x_, int64_t y_): x(x_), y(y_) {} |
| 67 | + |
| 68 | + Point operator - (const Point & p) const { |
| 69 | + return Point(x - p.x, y - p.y); |
| 70 | + } |
| 71 | + |
| 72 | + int64_t cross (const Point & p) const { |
| 73 | + return x * p.y - y * p.x; |
| 74 | + } |
| 75 | + |
| 76 | + int64_t cross (const Point & p, const Point & q) const { |
| 77 | + return (p - *this).cross(q - *this); |
| 78 | + } |
| 79 | + |
| 80 | + int half () const { |
| 81 | + return int(y < 0 || (y == 0 && x < 0)); |
| 82 | + } |
| 83 | +}; |
| 84 | + |
| 85 | +std::vector<std::vector<size_t>> find_faces(std::vector<Point> vertices, std::vector<std::vector<size_t>> adj) { |
| 86 | + size_t n = vertices.size(); |
| 87 | + std::vector<std::vector<char>> used(n); |
| 88 | + for (size_t i = 0; i < n; i++) { |
| 89 | + used[i].resize(adj[i].size()); |
| 90 | + used[i].assign(adj[i].size(), 0); |
| 91 | + auto compare = [&](size_t l, size_t r) { |
| 92 | + Point pl = vertices[l] - vertices[i]; |
| 93 | + Point pr = vertices[r] - vertices[i]; |
| 94 | + if (pl.half() != pr.half()) |
| 95 | + return pl.half() < pr.half(); |
| 96 | + return pl.cross(pr) > 0; |
| 97 | + }; |
| 98 | + std::sort(adj[i].begin(), adj[i].end(), compare); |
| 99 | + } |
| 100 | + std::vector<std::vector<size_t>> faces; |
| 101 | + for (size_t i = 0; i < n; i++) { |
| 102 | + for (size_t edge_id = 0; edge_id < adj[i].size(); edge_id++) { |
| 103 | + if (used[i][edge_id]) { |
| 104 | + continue; |
| 105 | + } |
| 106 | + std::vector<size_t> face; |
| 107 | + size_t v = i; |
| 108 | + size_t e = edge_id; |
| 109 | + while (!used[v][e]) { |
| 110 | + used[v][e] = true; |
| 111 | + face.push_back(v); |
| 112 | + size_t u = adj[v][e]; |
| 113 | + size_t e1 = std::lower_bound(adj[u].begin(), adj[u].end(), v, [&](size_t l, size_t r) { |
| 114 | + Point pl = vertices[l] - vertices[u]; |
| 115 | + Point pr = vertices[r] - vertices[u]; |
| 116 | + if (pl.half() != pr.half()) |
| 117 | + return pl.half() < pr.half(); |
| 118 | + return pl.cross(pr) > 0; |
| 119 | + }) - adj[u].begin() + 1; |
| 120 | + if (e1 == adj[u].size()) { |
| 121 | + e1 = 0; |
| 122 | + } |
| 123 | + v = u; |
| 124 | + e = e1; |
| 125 | + } |
| 126 | + std::reverse(face.begin(), face.end()); |
| 127 | + int sign = 0; |
| 128 | + for (size_t j = 0; j < face.size(); j++) { |
| 129 | + size_t j1 = (j + 1) % face.size(); |
| 130 | + size_t j2 = (j + 2) % face.size(); |
| 131 | + int64_t val = vertices[face[j]].cross(vertices[face[j1]], vertices[face[j2]]); |
| 132 | + if (val > 0) { |
| 133 | + sign = 1; |
| 134 | + break; |
| 135 | + } else if (val < 0) { |
| 136 | + sign = -1; |
| 137 | + break; |
| 138 | + } |
| 139 | + } |
| 140 | + if (sign <= 0) { |
| 141 | + faces.insert(faces.begin(), face); |
| 142 | + } else { |
| 143 | + faces.emplace_back(face); |
| 144 | + } |
| 145 | + } |
| 146 | + } |
| 147 | + return faces; |
| 148 | +} |
| 149 | +``` |
| 150 | +## Problems |
| 151 | + * [TIMUS 1664 Pipeline Transportation](https://acm.timus.ru/problem.aspx?space=1&num=1664) |
0 commit comments