问题
参见问题描述
算法实现
本文以Divide and Conquer
为例使用sml
来实现
定义1
critical point
,即关键点,记作cp
。它是用来刻画 s k y l i n e skyline skyline的点point sequence
,点序列,记作ps
。它包含二维平面上的一些点 p i ( x i , y i ) p_i(x_i,y_i) pi(xi,yi)critical point sequence
,即关键点序列,记作cps
(下同),它是刻画这些矩形的 s k y l i n e skyline skyline的一个最小 s e q u e n c e sequence sequence(并不只是简单的cp
序列)
e g \mathcal {eg} eg:对于一个矩形 b [ l , h , r ] b[l, h, r] b[l,h,r],它有两个 c p cp cp分别为 ( l , h ) (l, h) (l,h)和 ( r , 0 ) (r,0) (r,0), c p s b = < ( l , h ) , ( r , 0 ) > cps_{b}=<(l, h),(r,0)> cpsb=<(l,h),(r,0)>
概述
- D i v i d e Divide Divide:将 p s ps ps分成两半
- C o m b i n e Combine Combine:将两个 c p s cps cps按照横坐标 x i x_i xi升序排列合并为一个 c p s cps cps
//(*接受b[l,h,r]返回<(l,h),(r,0)>*)
fun getSkyline4One(b:int*int*int) =
let val P = (#3 b, 0)::[]
in (#1 b, #2 b)::P end;
//(*divide*)
//(*接受表xs,返回(xs/2, xs/2)*)
fun splitMid(xs) =
let fun split(x::xs, ys, n) =
if n=0 then (rev ys, x::xs) else splitMid(xs, x::ys, n-1)
in split(xs, [], length(xs) div 2) end
//(*combine*)
fun combine([], [], y, _, _) = rev y
//(*合并完成 ,返回p(xi, yi)按照xi升序排列的 P表*)
| combine([], sr::srs, y, hl, hr) = combine([], srs, sr::y, hl, hr)
//(*LeftSkyline已经合并(扫描)完成,将RightSkyline剩余点之间插入合并的表中*)
| combine(sl::sls, [], y, hl, hr) = combine(sls, [], sl::y, hl, hr)
//(*RightSkyline已经合并(扫描)完成,将LeftSkyline剩余点之间插入合并的表中*)
| combine(sl::sls, sr::srs, y, hl, hr) =
//(*一般的合并操作*)
let val (slx, sly) = sl
val (srx, sry) = sr
val (_, lasth) = if null y then (0, 0) else hd y
in if slx<srx then //(*将Left中的点插入合并表*)
let val maxh = if hr>sly then hr else sly
in if lasth=maxh then combine(sls, sr::srs, y, sly, hr)
else combine(sls, sr::srs, (slx, maxh)::y, sly, hr) end
else //(*slx>srx 将Right中的点插入合并表*)
let val maxh = if hl>sry then hl else sry
in if lasth=maxh then combine(sl::sls, srs, y, hl ,sry)
else combine(sl::sls, srs, (srx,maxh)::y, hl, sry) end
end;//(*if-else 均为尾递归 相当于扫描两个表 O(|L|+|R|), 由于splitMId |L|=|R|*)
fun MySkyline (bseq:(int*int*int) list):(int*int) list =
if List.length(bseq) = 1 then getSkyline4One(List.nth(bseq, 0))
else let val (seq1, seq2) = splitMid(bseq)
val (sl, sr) = (MySkyline(seq1), MySkyline(seq2)) //(* skylineLeft skylineRight *)
val P = combine(sl, sr, [], 0, 0)
in P end;
时间复杂度
fun MySkyline (bseq:(int*int*int) list):(int*int) list =
if List.length(bseq) = 1 then getSkyline4One(List.nth(bseq, 0)) //O(1)
else let val (seq1, seq2) = splitMid(bseq, [], length(bseq) div 2) //O(n/2)
val (sl, sr) = (MySkyline(seq1), MySkyline(seq2)) //2Work(n/2)
val P = combine(sl, sr, [], 0, 0) //O(n)
in P end;
除了更新高度的一些 O ( 1 ) O(1) O(1)操作, c o m b i n e combine combine完全等同于归并排序的 m e r g e merge merge,所以有,
W c o m b i n e ( n ) = W m e r g e ( n ) = O ( n ) W_{combine}(n)=W_{merge}(n)=O(n) Wcombine(n)=Wmerge(n)=O(n)
于是,
W M y S k y l i n e ( n ) = 2 W M y S k y l i n e ( n / 2 ) + W c o m b i n e ( n ) + W s p l i t M i d ( n ) + O ( 1 ) = 2 W M y S k y l i n e ( n / 2 ) + O ( n ) + O ( n 2 ) = 2 W M y S k y l i n e ( n / 2 ) + O ( n ) \begin{aligned} W_{MySkyline} (n) & =2W_{MySkyline}(n/2)+W_{combine}(n)+W_{splitMid}(n)+O(1)\\ & =2W_{MySkyline}(n/2)+O(n)+O(\frac n2)\\ & =2W_{MySkyline}(n/2)+O(n) \end{aligned} WMySkyline(n)=2WMySkyline(n/2)+Wcombine(n)+WsplitMid(n)+O(1)=2WMySkyline(n/2)+O(n)+O(2n)=2WMySkyline(n/2)+O(n)
由主定理
或Brick Tree Method
可得
W M y S k y l i n e ( n )