平面中N个点相关问题

平面中N个点相关问题
问题一:求N个点中斜率最大的两个点。
要解决该问题,我们首先证明一个结论:三个点a、b、c,若x a < x b < x c则斜率最大者必定是 ab或者是 bc,而不会是 ac。证明如下:
我们用k表示斜率,
不妨假设k ac > k ab,即(y c - y a)/(x c - x a) - (y b - y a)/(x b - x a) > 0
则可以推出x a * y b + x b * y c + x c * y a > x a * y c + x b * y a + x c * y b
那么可以得出k bc - k ac = (y c - y b)/(x c - x b) - (y c - y a)/(x c - x a)  = (x a * y b + x b * y c + x c * y a) - (x a * y c + x b * y a + x c * y b) > 0
所以可以知道如果ac斜率大于ab,那么它就不可能大于bc
同理可以得出若ac斜率大于bc,那么它就不可能大于ab
证毕。
有了上面的证明,我们就可以先对N个点的横坐标排序,然后再计算a[i]与a[i + 1]的斜率,取最大值即可。
代码略。

问题二:求N个点中距离最远的两点距离。
典型的求凸包直径问题,这里先讲解一下如何利用Graham scanning方法在O(nlogn)时间内求凸包,然后利用旋转卡壳法在O(n)时间内求凸包直径。
该问题面试中一般不会问到,太过复杂,不过应该学习这种思想。
1)Graham scanning求凸包:
首先:选取N个点中y坐标最小的点为P 0,若有多个点y坐标相同,则取x坐标最小的点为P 0,即P 0为坐标系中左下角的点。
然后:根据direction(P 0, P i, P j)来排序,direction()函数是求P 0P i向量和P 0P j向量的叉积,叉积的作用是判定P 0P i向量在P 0P j向量的逆时针方向还是顺时针方向,如果P 0P i X P 0P j > 0则说明P 0P i在P 0P j的顺时针方向,否则在逆时针方向。另外叉积的值的绝对值还表示以P 0P iP j三点组成的三角形的面积,因为P 0P i X P 0P j = |P 0P i| * |P 0P j| * sin ∠P iP 0P j,这个结论会在卡壳时用到。有了上面的知识,可以知道排序后的结果是所有节点围绕P 0以逆时针方向排列。
再次:将点P 0和点P 1入栈,然后从P 2到P n循环执行下面操作:若direction(P stack[top - 1], P stack[top], P i) < 0,则删除栈顶元素,即top--(因为排序的时候,如果两个节点对P 0的向量叉积若相等,则距离P 0远的节点排在后面,所以这里如果上述等式等于0的话则可以肯定P i到P stack[top - 1]的距离比P stack[top]到P stack[top - 1]的距离远,所以可以直接将P stack[top]出栈,当然也可以不出栈,因为某个在凸包多边形的某条边上的点,可以算作凸包的点,也可以去掉),否则P i进栈。直到P n判断完毕。
最后:栈stack中的所有点就是凸包多边形的点,并且从栈底到栈顶以逆时针排列。
上面算法表述的比较罗嗦,看下面的图示就明白了:
首先是排序,然后是P 0和P 1入栈:



然后是判断P 2是否应该入栈:



因为P 0P 1 X P 0P 2 > 0,所以P 2入栈:



然后判断P 3是否应该入栈:



因为P 1P 2 X P 1P 3 < 0,所以P 2出栈P 3入栈:



判断P 4是否应该入栈:



因为P 1P 3 X P 1P 4 > 0,所以P 4入栈:



判断P 5是否应该入栈:



因为P 3P 4 X P 3P 5 > 0,所以P 5入栈:



判断P 6是否应该入栈:



因为P 4P 5 X P 4P 6 < 0,所以p 5出栈P 6入栈:



最后p7入栈,形成最终的凸包:



通过以上图示过程可以清晰明白凸包的构建过程。证明过程比较复杂,详见《算法导论》。

你可能感兴趣的:(平面中N个点相关问题)