C++ 计算阵列中共线的三元组数
有人问我这个面试问题(C++,algos),我不知道如何解决它 给定一个数组,即包含N个不同点的笛卡尔坐标的Arr[N]计算三元组的数量(Arr[p],Arr[Q],Arr[R]),使得pC++ 计算阵列中共线的三元组数,c++,algorithm,C++,Algorithm,有人问我这个面试问题(C++,algos),我不知道如何解决它 给定一个数组,即包含N个不同点的笛卡尔坐标的Arr[N]计算三元组的数量(Arr[p],Arr[Q],Arr[R]),使得p
有什么想法吗?我可以使用什么算法来实现这一点?如果是二维点:如果(p,Q),(p,R)定义了相同的斜率,那么3个点(p,Q,R)是共线的m = (p.x - q.x) / (p.y - q.y) ; slope
不知何故,你需要检查所有可能的组合,并检查,一个有效的算法是技巧,因为第一个简单的是N*(N-1)*(N-2).我有这个解决方案告诉你是否有更好的 根据它们与x轴或任何其他所需轴(O(n*logn))的斜率对所有点进行排序。现在你要做的就是通过排序列表,找到具有相同斜率的点,无论是正方向还是负方向(这可以在线性时间内完成,即O(n))。假设你在一个例子中得到m个这样的点,然后将答案增加C(m,3) 总时间取决于实现C(m,3)的程度 但渐近O(N logN)
编辑:看到icCube的评论后,我意识到我们不能取任何轴。因此,对于上面定义的算法,将斜率计算点作为n个点之一(因此n次)应该是我的最佳猜测。但是它使算法N*N*Log(N)以下可能没有优化,但其复杂性是面试官要求的 首先为每对点(N²复杂性)创建一个(a、b、c)值列表 -->(a,b,c)表示直线的笛卡尔方程a*x+b*y+c=0 给定两个点及其坐标(xa,ya)和(xb,yb),计算(a,b,c)很简单。 要么你能找到解决问题的办法或 然后,可以将可解方程组改写为更一般的形式ya=alpha*xa+beta yb=alpha*xb+beta (if (xb-xa) != 0) alpha = (yb-ya)/(xb-xa) beta = ya - alpha*xa a = alpha b = -1 c = beta
然后对列表进行排序(N²日志(N²)复杂性,因此N²日志(N)复杂性) 迭代列表中的元素。如果两个连续元素相等,则对应点共线。N²复杂性 您可能希望添加最后一个操作来过滤重复的结果,但您应该很好,并且考虑到复杂性 编辑:我对算法进行了一些更新,同时对其进行了编码,使其更加简单和优化。来了a*x+b*y+c = 0
#include <map> #include <set> #include <vector> #include <iostream> struct StraightLine { double a,b,c; StraightLine() : a(0.),b(0.),c(0.){} bool isValid() { return a!=0. || b!= 0.; } bool operator<(StraightLine const& other) const { if( a < other.a ) return true; if( a > other.a ) return false; if( b < other.b ) return true; if( b > other.b ) return false; if( c < other.c ) return true; return false; } }; struct Point { double x, y; Point() : x(0.), y(0.){} Point(double p_x, double p_y) : x(p_x), y(p_y){} }; StraightLine computeLine(Point const& p1, Point const& p2) { StraightLine line; if( p2.x-p1.x != 0.) { line.b = -1; line.a = (p2.y - p1.y)/(p2.x - p1.x); } else if( p2.y - p1.y != 0. ) { line.a = -1; line.b = (p2.x-p1.x)/(p2.y-p1.y); } line.c = - line.a * p1.x - line.b * p1.y; return line; } int main() { std::vector<Point> points(9); for( int i = 0 ; i < 3 ; ++i ) { for( int j = 0; j < 3 ; ++j ) { points[i*3+j] = Point((double)i, (double)j); } } size_t nbPoints = points.size(); typedef std::set<size_t> CollinearPoints; typedef std::map<StraightLine, CollinearPoints> Result; Result result; for( int i = 0 ; i < nbPoints ; ++i ) { for( int j = i + 1 ; j < nbPoints ; ++j ) { StraightLine line = computeLine(points[i], points[j]); if( line.isValid() ) { result[line].insert(i); result[line].insert(j); } } } for( Result::iterator currentLine = result.begin() ; currentLine != result.end(); ++currentLine ) { if( currentLine->second.size() <= 2 ) { continue; } std::cout << "Line"; for( CollinearPoints::iterator currentPoint = currentLine->second.begin() ; currentPoint != currentLine->second.end() ; ++currentPoint ) { std::cout << " ( " << points[*currentPoint].x << ", " << points[*currentPoint].y << ")"; } std::cout << std::endl; } return 0; }
#包括 #包括 #包括 #包括 结构直线 { 双a、b、c; 直线() bool isValid(){返回a!=0。| | b!=0。} bool运算符其他。a)返回false; 如果(b
other.b)返回false; 如果(c second.size()second.end();++currentPoint) {
std::cout代替3个循环,即O(n³),预计算两点给出的所有直线的斜率。即O(n²)。然后比较这些斜率 您可以改进在计算期间或之后按斜率对线进行进一步排序的方法,即O(n logn)。之后,查找具有相同斜率的线就是O(n) 但如果您想知道哪些点是共线的,您可能需要通过实现数据结构来为此付出代价 我认为面试问题的关键不是给出完美的算法,而是在一个想法中识别和讨论问题 编辑: 暴力手段:Arr[p],Arr[Q]
#include <iostream> #include <vector> struct Point { int x, y; }; bool collinear(Point P, Point Q, Point R) { // TODO: have to look up for math ... see icCube's answer return false; } int main() { std::vector<Point> v; Point a; while (std::cin >> a.x >> a.y) { v.push_back(a); } int count = 0; for (int p = 0; p < v.size(); ++p) { for (int q = p+1; q < v.size(); ++q) { for (int r = q+1; r < v.size(); ++r) { if (collinear(v[p], v[q], v[r])) ++count; } } } std::cout << count << '\n'; return 0; }
#包括 #包括 结构点{intx,y;}; 布尔共线(点P、点Q、点R) { //TODO:必须查找数学…请参阅iCube的答案 返回false; } int main() { std::向量v; a点; 而(std::cin>>a.x>>a.y) { v、 推回(a); } 整数计数=0; 对于(int p=0;p
std::cout很容易看出,可以在O(n^2)时间内获得所有点对及其斜率和y截距。因此,输出为: IndexB斜率Y截距IndexA 当然,我们不会在IndexA=IndexB的位置插入任何条目 让我们对这个表进行索引(IndexB,Slope,Y),这会强制我们将插入作为O(log(n))插入到这个表中 在我们用新记录(B',S',Y',A')填写此表之后,我们检查是否已经有一个元素,使得现有表的B'=A和新记录的B!=A(意味着我们有一个唯一的三元组)匹配斜率和Y截距(意味着共线)。如果是这种情况,则< #include <iostream> #include <vector> struct Point { int x, y; }; bool collinear(Point P, Point Q, Point R) { // TODO: have to look up for math ... see icCube's answer return false; } int main() { std::vector<Point> v; Point a; while (std::cin >> a.x >> a.y) { v.push_back(a); } int count = 0; for (int p = 0; p < v.size(); ++p) { for (int q = p+1; q < v.size(); ++q) { for (int r = q+1; r < v.size(); ++r) { if (collinear(v[p], v[q], v[r])) ++count; } } } std::cout << count << '\n'; return 0; }
#include <iostream> #include <vector> #include <set> #include <stdlib.h> #include <math.h> using namespace std; #define ADD_POINT(xparam,yparam) { point x; x.x = xparam; x.y = yparam; points.push_back(x); }; #define EPSILON .001 class line { public: double slope; double y; int a; int b; bool operator< (const line &other) const{ if(this->a < other.a) return true; else if(this->a==other.a){ if(this->slope-other.slope < -EPSILON) return true; else if(fabs(this->slope-other.slope) < EPSILON){ if(this->y-other.y < -EPSILON) return true; else return false; }else return false; }else return false; } line(double slope, double y, int a, int b){ this->slope = slope; this->y = y; this->a = a; this->b = b; } line(const line &other){ this->slope = other.slope; this->y = other.y; this->a = other.a; this->b = other.b; } }; class point { public: double x; double y; }; int main(){ vector<point> points; ADD_POINT(0,0); ADD_POINT(7,28); ADD_POINT(1,1); ADD_POINT(2,3); ADD_POINT(2,4); ADD_POINT(3,5); ADD_POINT(3,14); ADD_POINT(5,21); ADD_POINT(9,35); multiset<line> lines; for(unsigned int x=0;x<points.size();x++){ for(unsigned int y=0;y<points.size();y++){ if(x!=y){ // No lines with the same point point a = points[x]; point b = points[y]; double slope = (a.y-b.y)/(a.x-b.x); double yint; yint = a.y-a.x*slope; line newline(slope,yint,x,y); lines.insert(newline); } } } for(multiset<line>::const_iterator p = lines.begin(); p != lines.end(); ++p){ //cout << "Line: " << p->a << " " << p->b << " " << p->slope << " " << p->y << endl; line theline = *p; line conj(theline.slope,theline.y,theline.b,-1); multiset<line>::iterator it; pair<multiset<line>::iterator,multiset<line>::iterator> ret; ret = lines.equal_range(conj); for(it = ret.first; it!=ret.second; ++it){ //cout << " Find: " << it->a << " " << it->b << " " << it->slope << " " << it->y << endl; int a = theline.a; int b = theline.b; int c = it->b; if(a < b && b < c){ cout << a << " " << b << " " << c << std::endl; } } } //cout << points[0].x << std::endl; }
import java.util.*; public class CollinearTriplets { public static void main(String[] args) { Point2d A[] = new Point2d[8]; A[0] = new Point2d(0, 0); A[1] = new Point2d(1, 1); A[2] = new Point2d(2, 2); A[3] = new Point2d(3, 3); A[4] = new Point2d(3, 2); A[5] = new Point2d(4, 2); A[6] = new Point2d(5, 1); A[7] = new Point2d(4, 4); System.out.println(countCollinear(A)); } public static int factorial(int n) { int fact = 1; for (int i = 1; i <= n; i++) { fact = fact * i; } return fact; } private static int combinations(int n, int r) { return factorial(n) / (factorial(n - r) * factorial(r)); } private static long countCollinear(Point2d[] points) { Map<Line, Set<Point2d>> lineToPoints = new HashMap<>(); long result = 0; for (int i = 0; i < points.length; i++) { for (int j = i + 1; j < points.length; j++) { double slope = 0d, xIntercept, yIntercept; // Default slope paralell to y-axis if (points[i].x == points[j].x) { slope = Double.MAX_VALUE; // Horizontal slope parallel to x-axis } else if (points[i].y != points[j].y) { xIntercept = points[j].x - points[i].x; yIntercept = points[j].y - points[i].y; slope = yIntercept / xIntercept; } Line currLine = new Line(points[i], slope); if (Objects.isNull(lineToPoints.get(currLine))) { lineToPoints.put(currLine, new HashSet<>()); } lineToPoints.get(currLine).add(points[i]); lineToPoints.get(currLine).add(points[j]); } } for (Line line : lineToPoints.keySet()) { int size = lineToPoints.get(line).size(); if (size >= 3) { result = result + combinations(size, 3); } } return result; } /** * Line which contains the starting point and slope so that you can identify exact line * equals method is overridden to check whether any new line is coinciding or parallel */ static class Line { Point2d point; double slope; public Line(Point2d point, double slope) { this.point = point; this.slope = slope; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Line)) return false; Line line = (Line) o; if (line.slope == this.slope) return ((((double) (line.point.y - this.point.y)) / (line.point.x - this.point.x)) == this.slope); return false; } @Override public int hashCode() { return Objects.hash(slope); } } static class Point2d { int x; int y; public Point2d(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Point2d)) return false; Point2d point2d = (Point2d) o; return x == point2d.x && y == point2d.y; } @Override public int hashCode() { return Objects.hash(x, y); } } }