问题一:求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
∠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) 编辑 收藏 引用 所属分类:
算法基础