随笔-80  评论-24  文章-0  trackbacks-0
问题一:求N个点中斜率最大的两个点。
要解决该问题,我们首先证明一个结论:三个点a、b、c,若xa < xb < xc则斜率最大者必定是ab或者是bc,而不会是ac。证明如下:
我们用k表示斜率,
不妨假设kac > kab,即(yc - ya)/(xc - xa) - (yb - ya)/(xb - xa) > 0
则可以推出xa * yb + xb * yc + xc * ya > xa * yc + xb * ya + xc * yb
那么可以得出kbc - kac = (yc - yb)/(xc - xb) - (yc - ya)/(xc - xa)  = (xa * yb + xb * yc + xc * ya) - (xa * yc + xb * ya + xc * yb) > 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坐标最小的点为P0,若有多个点y坐标相同,则取x坐标最小的点为P0,即P0为坐标系中左下角的点。
然后:根据direction(P0, Pi, Pj)来排序,direction()函数是求P0Pi向量和P0Pj向量的叉积,叉积的作用是判定P0Pi向量在P0Pj向量的逆时针方向还是顺时针方向,如果P0Pi X P0Pj > 0则说明P0Pi在P0Pj的顺时针方向,否则在逆时针方向。另外叉积的值的绝对值还表示以P0PiPj三点组成的三角形的面积,因为P0Pi X P0Pj = |P0Pi| * |P0Pj| * sin∠PiP0Pj,这个结论会在卡壳时用到。有了上面的知识,可以知道排序后的结果是所有节点围绕P0以逆时针方向排列。
再次:将点P0和点P1入栈,然后从P2到Pn循环执行下面操作:若direction(Pstack[top - 1], Pstack[top], Pi) < 0,则删除栈顶元素,即top--(因为排序的时候,如果两个节点对P0的向量叉积若相等,则距离P0远的节点排在后面,所以这里如果上述等式等于0的话则可以肯定Pi到Pstack[top - 1]的距离比Pstack[top]到Pstack[top - 1]的距离远,所以可以直接将Pstack[top]出栈,当然也可以不出栈,因为某个在凸包多边形的某条边上的点,可以算作凸包的点,也可以去掉),否则Pi进栈。直到Pn判断完毕。
最后:栈stack中的所有点就是凸包多边形的点,并且从栈底到栈顶以逆时针排列。
上面算法表述的比较罗嗦,看下面的图示就明白了:
首先是排序,然后是P0和P1入栈:



然后是判断P2是否应该入栈:



因为P0P1 X P0P2 > 0,所以P2入栈:



然后判断P3是否应该入栈:



因为P1P2 X P1P3 < 0,所以P2出栈P3入栈:



判断P4是否应该入栈:



因为P1P3 X P1P4 > 0,所以P4入栈:



判断P5是否应该入栈:



因为P3P4 X P3P5 > 0,所以P5入栈:



判断P6是否应该入栈:



因为P4P5 X P4P6 < 0,所以p5出栈P6入栈:



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



通过以上图示过程可以清晰明白凸包的构建过程。证明过程比较复杂,详见《算法导论》。
posted on 2012-09-07 12:56 myjfm 阅读(548) 评论(0)  编辑 收藏 引用 所属分类: 算法基础

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理