算法设计与分析基础(六)
练习题
练习一
-
对 f(n)=n, 并且 a = b 的情况,证明递归方程T(n)=aT(n/b)+f(n)的解为O(nlogn).
证明:
练习二
-
设计求解n个数字之和的分治算法,给出伪代码描述并分析算法的计算复杂度(每次分为2个子问题)。
算法思想:将n个待相加的元素分割成2个大致相等的子集合A、B;对每一个子集合分别递归求和;再将每个子集的和相加。当n等于1时递归终止,此时所求之和为该元素自身。
伪代码:
Algorithm Sum (A[0 ... n-1]) //输入:n个数字 //输出:n个数字的和 if n>1 then sum := Sum(A[0... └ n/2 ┘-1]) + Sum(A[ └n/2┘...n-1]); return sum else return A[0]
时间复杂度分析:设T(n) 为算法计算n个数字之和所花费的基本运算次数。此时的基本运算为加法。显然,当n=1时,T(n)=0; n>1时,不失一般性,假设n=2k,其中k为某个正整数,则
算法也可以按照如下形式描述:Algorithm Sum (arr, i, j) //arr为待求和的数组,i指向第一个元素,j指向最后一个元素 Begin m := ⌈ (i+j)/2 ⌉ if i=j then return arr[i] else return Sum(arr, i, m) + Sum(arr, m+1, j) end
练习三
-
假设R与S 分别为具有r个与s个元素的有序表,其中s ≤ log r . 设计一个最坏情况下O(log2r)时间的算法, 将R与S合并成一个有序表.
算法思想:
将b1, b2, …, bs 依次插入到有序表R中。对于元素bj , 在R上使用二分查找方法,找到bj所要插入的位置。假设bj插入后的状态为
a1, a2, …, ak, bj, ak+1, ak+2, …, ar,
由于bj≤bj+1,则将bj+1插入R时的工作可在有序子序列ak+1, ak+2, …, ar上进行,从而对S中的每个元素在R上实施二分查找插入方法的序列长度始终不超过r。根据二分查找算法的复杂度为O(logr),得知归并R与S的算法复杂度为O(s·logr)。由于s≤logr,得出算法复杂度为 O(log2r)。
算法形式化描述:
Algorithm Merger (R, S) Begin 1. Temp := R; 2. for i := 1 to s do 2.1. k := Binsearch(bi , Temp); 2.2. 将bi 插入到R中,并更新R ; 2.3. Temp := (ak+1, ak+2, ..., ar ); end of for; 3. return(R); end
练习四
-
给定长度为n的表,表中的所有元素已构成k个有序段, 设计一个计算复杂度为O(n log k)的算法完成对表元素的排序。给出算法的形式化描述与复杂度分析。
解法一:
解:假定k个有序段依次为L1,L2,…,Lk,其中每个Li的长度为 li,1 ≤ i ≤ k,则l1+l2+…+lk = n。
算法形式化描述:
Algorithm Sort(A) begin If k = 1 then return(A) else begin 1. m := ┍k/2┑ ; 2. B := {L1,L2,...,Lm}; 3. C := {Lm+1,Lm+2,...,Lk}; 4. Sort(B); 5. Sort(C); 6. Merge(B,C,A); end; end.
解法二:
以k个有序段的首位元素建立堆(建立最小堆),在此堆上连续地进行删除操作以达到归并的目的。当堆顶元素被删除后,其所在的有序段增补一个次小元素入堆并完成堆化。
算法描述性分析:
//Input: k个链表,记为A[k] //Output: 有序序列B[n] Begin For i:=1 to k do B[i]:= A[i].value Createheap(B,index) //index记录最小堆中元素对应于A的索引 While i != n do Begin If(A[index[0]].next != 0) then B[0] := A[index[0]].next.value Else: B[0] := B[k] Swap(index[0],index[k]) k:=k-1 k:=k-1 AdjustHeap(B,0,k) end end
时间复杂度分析:
假设k个有序段均为非降序排列的。
(1)取k个元素建立最小堆,这k个元素分别是k个有序段的第一个元素。建立最小堆的时间复杂度为O(k)。
(2)此时,该最小堆的堆顶元素就是k个有序段中最小的那个元素,将它取出返回,时间复杂度O(1)。
(3)若堆顶元素所在有序段链表不为空,则取下一个元素放到堆顶位置,进行堆调整,堆调整时间复杂度 O(logk)。若为空,此子链表已经被合并完毕,则删除最小堆的堆顶元素,此时最小堆的结点数减小了1 ,删除指定元素的时间复杂度O(logk)。
(4)重复步骤(2)、(3)n-k次。总的时间复杂度是O(k)+O(nlogk)即O(nlogk)。