Skip to content

Commit 2f5bcb3

Browse files
authored
Add Convex Hull in C++ (#5537)
1 parent c210ff0 commit 2f5bcb3

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#include <algorithm>
2+
#include <iostream>
3+
#include <sstream>
4+
#include <string>
5+
#include <vector>
6+
7+
struct Point
8+
{
9+
int x{};
10+
int y{};
11+
12+
Point() = default;
13+
Point(int x_, int y_) : x(x_), y(y_)
14+
{
15+
}
16+
17+
bool operator<(const Point &other) const
18+
{
19+
if (x != other.x)
20+
{
21+
return x < other.x;
22+
}
23+
return y < other.y;
24+
}
25+
};
26+
27+
[[noreturn]] void usage()
28+
{
29+
std::cerr
30+
<< R"(Usage: please provide at least 3 x and y coordinates as separate lists (e.g. "100, 440, 210"))"
31+
<< '\n';
32+
std::exit(1);
33+
}
34+
35+
std::vector<int> parseList(const std::string &input)
36+
{
37+
std::vector<int> out;
38+
std::stringstream ss(input);
39+
40+
for (std::string token; std::getline(ss, token, ',');)
41+
{
42+
auto start = token.find_first_not_of(" \t");
43+
if (start == std::string::npos)
44+
{
45+
usage();
46+
}
47+
48+
const auto end = token.find_last_not_of(" \t");
49+
token = token.substr(start, end - start + 1);
50+
51+
size_t pos = 0;
52+
int value = 0;
53+
54+
try
55+
{
56+
size_t pos = 0;
57+
const int value = std::stoi(token, &pos);
58+
59+
if (pos != token.size())
60+
{
61+
usage();
62+
}
63+
64+
out.push_back(value);
65+
}
66+
catch (...)
67+
{
68+
usage();
69+
}
70+
}
71+
72+
return out;
73+
}
74+
75+
std::vector<Point> parseCoordinates(const std::string &xs,
76+
const std::string &ys)
77+
{
78+
auto x = parseList(xs);
79+
auto y = parseList(ys);
80+
81+
if (x.size() != y.size() || x.size() < 3)
82+
{
83+
usage();
84+
}
85+
86+
std::vector<Point> pts;
87+
pts.reserve(x.size());
88+
89+
std::transform(x.begin(), x.end(), y.begin(), std::back_inserter(pts),
90+
[](int a, int b) { return Point{a, b}; });
91+
92+
return pts;
93+
}
94+
95+
long long cross(const Point &a, const Point &b, const Point &c)
96+
{
97+
return 1LL * (b.x - a.x) * (c.y - a.y) - 1LL * (b.y - a.y) * (c.x - a.x);
98+
}
99+
100+
std::vector<Point> convexHull(std::vector<Point> pts)
101+
{
102+
if (pts.size() < 3)
103+
{
104+
usage();
105+
}
106+
107+
std::sort(pts.begin(), pts.end());
108+
109+
std::vector<Point> lower, upper;
110+
lower.reserve(pts.size());
111+
upper.reserve(pts.size());
112+
113+
auto build_half = [&](auto begin, auto end, auto &hull) {
114+
for (auto it = begin; it != end; ++it)
115+
{
116+
const auto &p = *it;
117+
118+
while (hull.size() >= 2 &&
119+
cross(hull[hull.size() - 2], hull.back(), p) <= 0)
120+
{
121+
hull.pop_back();
122+
}
123+
124+
hull.push_back(p);
125+
}
126+
};
127+
128+
build_half(pts.begin(), pts.end(), lower);
129+
build_half(pts.rbegin(), pts.rend(), upper);
130+
131+
lower.pop_back();
132+
upper.pop_back();
133+
134+
lower.insert(lower.end(), std::make_move_iterator(upper.begin()),
135+
std::make_move_iterator(upper.end()));
136+
137+
return lower;
138+
}
139+
140+
int main(int argc, char *argv[])
141+
{
142+
if (argc != 3)
143+
{
144+
usage();
145+
}
146+
147+
const auto hull = convexHull(parseCoordinates(argv[1], argv[2]));
148+
149+
for (const auto &[x, y] : hull)
150+
{
151+
std::cout << '(' << x << ", " << y << ")\n";
152+
}
153+
}

0 commit comments

Comments
 (0)