折线段为LineString,即给出顺序连接线段的端点
除了暴力枚举两折线段的所有边,还有没有时间复杂度更低的算法
有一种基于扫描线的算法可以判断两条折线段是否相交,时间复杂度为O((n+m)log(n+m)),其中n和m分别为两条折线段的点数。下面是具体的实现思路:
下面是一个C++的实现示例,使用了STL中的set和pair来实现线段集合:
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <utility>
using namespace std;
struct Point {
int x;
int y;
Point(int x, int y): x(x), y(y) {}
};
struct Segment {
Point p1;
Point p2;
Segment(Point p1, Point p2): p1(p1), p2(p2) {}
};
bool cmp(const Point& p1, const Point& p2) {
if (p1.x != p2.x) {
return p1.x < p2.x;
} else {
return p1.y < p2.y;
}
}
bool intersect(const Segment& s1, const Segment& s2) {
int x1 = s1.p1.x, y1 = s1.p1.y;
int x2 = s1.p2.x, y2 = s1.p2.y;
int x3 = s2.p1.x, y3 = s2.p1.y;
int x4 = s2.p2.x, y4 = s2.p2.y;
if (max(x1, x2) < min(x3, x4) || max(x3, x4) < min(x1, x2) ||
max(y1, y2) < min(y3, y4) || max(y3, y4) < min(y1, y2)) {
return false;
}
int c1 = (x2-x1)*(y3-y1) - (y2-y1)*(x3-x1);
int c2 = (x2-x1)*(y4-y1) - (y2-y1)*(x4-x1);
int c3 = (x4-x3)*(y1-y3) - (y4-y3)*(x1-x3);
int c4 = (x4-x3)*(y2-y3) - (y4-y3)*(x2-x3);
return (c1*c2 <= 0) && (c3*c4 <= 0);
}
int main() {
// 读入两条折线段的端点
int n, m;
cin >> n >> m;
vector<Point> points;
for (int i = 0; i < n+m; i++) {
int x, y;
cin >> x >> y;
points.push_back(Point(x, y));
}
// 按照x坐标从小到大排序,如果x坐标相等则按照y坐标从小到大排序
sort(points.begin(), points.end(), cmp);
// 扫描线
set<pair<Segment, int>> segments;
for (int i = 0; i < n+m; i++) {
// 判断当前点是否是某条折线段的端点
if (i > 0 && points[i].x == points[i-1].x && points[i].y == points[i-1].y) {
continue;
}
int j = i+1;
while (j < n+m && points[j].x == points[i].x && points[j].y == points[i].y) {
j++;
}
if (j-i == 2) {
// 当前点是一条线段的端点
Segment seg(points[i], points[j-1]);
if (i < n) {
// 第一条折线段
segments.insert(make_pair(seg, 1));
} else {
// 第二条折线段
segments.insert(make_pair(seg, 2));
}
}
i = j-1;
}
// 判断相交关系
for (auto it1 = segments.begin(); it1 != segments.end(); it1++) {
for (auto it2 = next(it1); it2 != segments.end(); it2++) {
if (it1->second != it2->second && intersect(it1->first, it2->first)) {
cout << "Two segments intersect" << endl;
return 0;
}
}
}
cout << "Two segments do not intersect" << endl;
return 0;
}
这个程序先将所有点按照x坐标从小到大排序,然后扫描线从左往右依次扫描每个点,将每条线段加入到一个set中。最后,遍历set中的所有线段,判断它们是否相交。