算法设计与分析基础(九)

算法设计与分析基础(九)

练习题

习题一

a. 为一个分治算法编写伪代码,该算法同时求出一个n元素数组的最大元素 和最小元素的值。

b. 假设n = 2k,为该算法的键值比较次数建立递推关系式并求解。

c. 请将该算法与同样问题的蛮力算法做一个比较。

解答:

a. 算法思想

将数组A划分为大小大致相等的两个子数组;递归地对这两个子数组求最大元素和最小元素;将两个子数组的最大元素进行比较,返回数组A的最大元素,将其最小元素进行比较,返回数组A的最小元素;当n不超过2时,递归终止,此时最多通过一次比较就可得到最大元素与最小元素。

假设 max {a, b} , min {a, b} 分别为通过一次比较产生出两者之间的最大者与最小者的标准过程。

伪代码:

Algorithm Max-Min (A, M, N)
//输入:n个元素的数组A
//输出:数组A的最大元素M和最小元素N
begin
      if n = 1 then  M := A[0]  and  N := A[0]
      else if n = 2 then M=max{A[0], A[1]}  and  N :=                 min{A[0], A[1]}
      else begin
               B := A[0..┗n/2],  C := A[ ┗n/2+1..n-1];
               Max-Min(B, M0, N0);
               Max-Min(C, M1, N1);
               M := max{ M0, M1};
               N := min{N0, N1};
           end;
end.

b.设 T(n) 为算法求n个元素数组的最大元素和最小元素的基本运算次数,此时的基本运算为比较。当n=2时,T(2)=1,n=1时,T(1)=0 , 则n≥2时有:

image-20211214094710000

c. 当n大于1时,用蛮力算法找到最大元素并取出需要n-1次比较,接着在剩下的n-1个元素中找出最小元素需要n-2次比较,总的比较次数为2n-3次,因此分治法在计算效率上比蛮力法要快。

习题二

A[0…n-1]是一个n个不同实数构成的数组。如果i < j,但是A[i] > A[j],则这对元素(A[i],A[j])被称为一个倒置(inversion)。设计一个O(nlogn)算法来计算 数组中的倒置数量。

算法思想:

对数组A实施合并排序,在排序的过程中统计元素倒置的个数。递归步骤为:

  1. A划分为两个大小基本相等的子数组BC

  2. 分别递归地对BC进行排序并计算各自元素的倒置数;

  3. 对有序数组BC进行归并, 并统计两个有序数组归并时存在的元素倒置数;

当数组A的大小不超过2时,算法递归终止、直接排序并统计倒置个数。

伪代码:

Algroithm Inversion (A[0..n-1], a)
//输入数组A[0..n-1]
//输出n个数的倒置数量a
begin
if n=1 then return (0)
else begin
          B := A[0..⌊n/2], C := A[⌊n/2+1 .. n-1]  // 将A划分成子数组B与C 
	       Inversion (B, b);  //对B进行排序并计算倒置数量b
	       Inversion (C, c);  //对C进行排序并计算倒置数量c
	       Merge (B, C, A, d); //将有序数组B与C 合并成A并计算倒置数d;
	        a := b + c + d;
end; 
end.

其中**Merge(B, C, A, d)过程中倒置数的计算依据如下:BC中各自当前最小的元素进行比较,较小者移入A中,并对倒置数进行累加。 若较小者来自数组B,则不产生倒置;若较小者来自数组C,则将产生倒置,并且与此较小者相关的倒置数量为B中的还没有移入A的剩余元素个数。伪代码描述如下:

Procedure Merge (B[0..p-1], C[0..q-1], A[0..p+q-1], d) 
// 将有序数组B与C合并成有序数组A,并计算元素的倒置数d.
begin
   i: =0, j:=0, k:=0; d:=0;
   while i<p and j<q do
        if B[i] ≤ C[j] then A[k] :=B[i], i:=i+1//如果最小的是B产生的
                   else A[k] := C[j], j :=j+1, d := d + (p – i);//否则就是C产生的,倒置数是前面的B中的还没有移入A的剩余元素个数
        k := k+1;
end of while
    if i=p then // 数组B中的元素已经处理完毕,无需倒置追加
             copy C[j..q-1] to A[k..p+q-1] 
         else // 数组C中的元素已经处理完毕,无需倒置追加
             copy B[i..p-1] to A[0..p+q-1];
end.

复杂度分析:

​ 由于算法是在合并排序的过程中进行倒置数的计算,从而算法的计算复杂度与合并排序复杂度相同,即为O(n log n)。

习题三

螺钉和螺姆问题

image-20211214151924810

算法思想:

假设螺母和螺钉分别存放在数组NutBolt中。运用快速排序的思想,在Nut中随机选择一个螺母u,通过螺母与螺钉匹配的检测,对Bolt中的螺钉进行划分,生成螺钉的子集合L*、{u*} 与 R*,其中L*中的螺钉小于螺母u,R*中的螺钉大于螺母u,而u*为与u成功匹配的螺钉。然后再用螺钉u*对Nut中的螺母进行类同的划分,生成螺母的子集合 L、{u} 与 R。这样在整个NutBolt上进行匹配的问题就可继续在L与 L*、 R 与 R* 上递归进行。当子集合中只剩下一个元素时递归终止,此时的螺母和螺钉一定互相匹配。

令Partition(Nut,u*) 与 Partition(Boltu)为上述的划分过程,并分别返回划分后的uu*,以及所在的位置 s,算法的形式化描述如下:

Algorithm Pairing (Nut[0..n-1], Bolt[0..n-1])
//输入:n个直径各不相同的螺母和n个相应的螺钉
//输出:每一对匹配的螺钉和螺母
Begin
    If n = 1 then return (Nut[0], Bolt[0]);
else begin
      //在Nut中随机选择一个螺母u; 
       (s, u*) := Partition(Bolt[0..n-1], u);
       (s, u) := Partition(Nut[0..n-1], u*); 
       Pairing(Nut[0.. s-1], Bolt[0..s-1]);
       Pairing(Nut[s+1..n-1], Bolt[s+1..n-1]);
    end;
end.

时间复杂度:

算法的基本操作为元素之间的比较。算法完全在快速排序的框架下进行,在递归前仅仅增加了一次划分,而每次划分的代价为O(n),在量级上与快速排序中的划分过程的代价相同,依据快速排序算法的平均复杂度为O(nlogn) 的结果,得知上述算法的平均复杂度依然是O(nlogn)。

习题四

设计一个分治算法来计算二叉树的层数(具体来说,对于空树和单节点树, 该算法应该返回0和1)。这个算法的效率类型是怎样的?

算法思想:

对于给定的二叉树T,如果二叉树为空,则返回0;若不为空,则递归地计算其左子树的层数和右子树的层数,取其中最大者加1,即为此二叉树的层数。

伪代码:

Algorithm BinaryTree(T)
//输入:给定的二叉树T
//输出:T的层数
Begin
    if  T = Ø then return 0;
    else return max{BinaryTree(Tleft), BinaryTree(Tright)}+1;
end.

时间复杂度:

求二叉树的高度需要遍历数的所有节点,所以该算法的时间复杂度为O(n)。

习题五

下列算法试图计算一颗二叉树的叶子数。

算法 LeafCounter(T)
//递归计算二叉树的叶子数
//输入:一颗二叉树T
//输出:T的叶子树
    if T = Ø  return 0
    else return LeafCounter(Tleft)+LeafCounter(Trigth)

该算法正确吗?如果正确,请证明;否则请改正!

**解答:**当树为单节点树时,由题目所给的算法计算得该叶子节点数为0,所以该算法不正确。对算法的改正如下:

算法 LeafCounter (T)
//输入:一颗二叉树T
//输出:T的叶子树
if T = Ø return 0else if T = 1 then return 1;
    else return LeafCounter(Tleft) + LeafCounter(Trigth)

习题六

为最近点对问题的一维版本(求一个给定的n整数集合中最接近的两个数)设计一个直接基于分治技术的算法,并确定它的效率类型。

解答:

算法思想:假设n整数集合S中这些整数点已经按增序排列,将这个有序划分为大小大致相等两个子集合S1、S2;递归地找出子集合S1、S2中的最近点对和一个点在S1另一点在S2的最近点对,比较上述三个点对的距离,找出最小点对;当n小于等于2时递归终止。

伪代码:

Algorithm ClosestPoints (S[0..n-1])
//输入:含n个整数的集合S
//输出:集合S中的最近点对的距离
Begin
    if n = 1 then return 无穷大;
    else if n = 2 return S[1]-S[0];
     else return min{ ClosestPoints (S[0..┗n/2]), ClosestPoints (S[ ┗n/2+1..n-1]), 		S[┗n/2+1]-S[┗n/2]}
end.

复杂度分析:

假设n=2k,基本操作为比较。T(n)=2T(n/2)+C,其中C为常数,计算得时间复杂度为O(n);若给定的数组无序,因此需要进行排序操作,此时总时间复杂度为O(nlog n)

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值