算法设计练习题(1)——分治法

  • 1、 给定一个数组A,任务是设计一个算法求得数组中的“主元素”,即在数组中个数超过数组总元素个数一半的元素。但是数组中元素的数据类型可能是复杂类型,这意味着数组中的元素进能够比较是否相等而不存在序关系,设对于两个元素A[i]和A[j],判定是否A[i]=A[j]需要常数时间。
    (1) 设计一个时间复杂性为O(n log n)的算法解此问题
    (2) 设计一个时间复杂性为O(n)的算法解此问题.
(1)分治法:
算法:
1、如果A中只有一个元素,则该元素为主元素。否则,将数组A分为两部分A[i....mid],A[mid+1........A.length],分别求主元素p,q,则A的主元素一定为p或q中的一个
2、遍历数组A,求出与p,q相等的元素个数,如果>A.length/2,则为主元素。
T(n) = 1     n==1;
T(n) = 2T(n/2)+O(n)
得T(n) = O(nlogn)
(2)算法:
数列排序后位于最中间的那个数。如果一个数列有主元素,那么必然是其中位数。
求一个数列有没有主元素,只要看中位数是不是主元素。
找中位数的方法:
选择一个元素作为划分起点,然后用快速排序的方法将小于它的移动到左边,大于它的移动到右边。
这样就将元素划分为两个部分。
此时,划分元素所在位置为k。
如果k>n/2,那么继续用同样的方法在左边部分找;
如果k<n/2就在右边部分找;k=n/2就找到了中位元素。

可以在平均时间复杂度为O(n)的时间内找出一个数列的中位数。
然后再用O(n)的时间检查它是否是主元素。
判断是否是主元素的伪代码:
master(A):
      len ← length[A]
      median ← randomizedSelect(A , 0 , n - 1 , n/2); //求中位数,平均时间复杂度为O(n)
      cnt ← 0
      //计算中位数出现次数
      for i ← 0 to len – 1
        do if A[i] = median
               then cnt ← cnt + 1
      if cnt > n/2
        then print "主元素:" +median + "出现次数:" + cnt
        else print "无主元素"

  • 2、对于给定的n个元素的数组A[1…n],要求从中找出第k小的元素。请设计分治算法解决这个问题,要求算法的平均时间复杂度是O(n)。答案要求包含以下内容:(1)用简明的自然语言表述算法的基本思想;(2)用伪代码描述算法;(3)分析算法的时间复杂度。

注意是平均时间复杂度E(T(n))

算法:
1、取第一个元素为a,遍历剩下元素,将>=a的元素添加到序列Big[]中,<a的元素添加到序列Small[]中。
2、求Small.length,如果Small.length == k,则该元素a为所求元素;
	如果Small.length < k,则求Big[]中的第k-1 - Small.length小的元素
	如果>k,则求Small[]中的第k小的元素。
伪代码:
Search(A)
Input :String[m....n],k
Output:A[m...n]中的第K小的元素
a = A[1]
for i = 2 to n
	if A[i] >= a then
		add A[i] to Big[]
	else
		add A[i] to Small[]
if Small.length == k then
	Output a
	END
else if Small.length < k then
	Search(Small[],k)
else
	Search(Big[],k-1-Small.length)

该算法最坏复杂度为O(n2),平均复杂度纠结如下:
在这里插入图片描述在这里插入图片描述

  • 3、给定实数数组A[1:n],试设计一个分治算法找出其中的最小元素和最大元素,使得比较操作的总次数严格小于2(n-1)。答案要求包含以下内容:(1)用简明的自然语言表述算法的基本思想;(2)用伪代码描述算法;(3)分析算法的时间复杂度。
(1)算法:
求A[1...n]的max and min 即求A[1...mid]和A[mid+1....n]的最大值和最小值,再比较后得到
(2)伪代码:
MAXMIN(A)
Input:A[i..j]
Output:max,min
if j-i+1 == 1 then  Output A[i],A[i];END
if j-i+1 == 2 then
	if A[i]<A[j] then Output A[j],A[i];END
	else 
		Output A[i],A[j];END
mid =(i-j+1)/2
M1,m1 = MAXMIN(A[i...mid])
M2,m2 = MAXMIN(A[mid+1....n])
m = min(m1,m2)
M = max(M1,M2)
Output M,m
(3)分析
递归方程
T(n) = 0   n==1
T(n) = 1  n ==2
T(n) = 2T(n/2) + 2       n>2
       =2+2(2+2T(n/4))
       ......
       =2+2^1+2^2+.....+2^i+2^iT(n/2^i)
       且此时n/2^i == 2
       =3n/2-2 < 2(n-1)
  • 4、有长度为N的数组A、B,每个数组中存储的浮点数已经升序排列。设计一个O(logn)时间的分治算法,找出这2n个数的中位数。证明算法的正确性。
1、如果n==1,则mid = (A[0]+B[0])/2
2、如果n>1,对A求中位数midA,对B求中位数midB
	如果midA  == midB,则最终的中位数为midA;
	如果midA < midB, 则求A[ceil(n/2)....n]和B[1...ceil(n/2)]这两个数组的中位数T(n+1)
	如果midA > midB,则求A[1...ceil(n/2)]和B[ceil(n/2)...n]这两个数组的中位数
递归方程:
T(2n) = 1   n==1;
T(2n) = T(n+1) +1   n>1

下面证T(n) = O(logn)

T(1)满足,设 m < 2n 时满足T(m) = O(logm),对 m = n+1,也满足
∴ T(n+1) <= clog(n+1)
∴ T(2n) <= clog(n+1) +1
		<= clog(2n/(4/3)) +1  = clog(2n) -clog(4/3)+1
		当-clog(4/3)+1 <= 0 时
		<= clog(2n)
由数学归纳法,T(n) = o(logn)
  • 5、设计分治算法求解下述问题:
    输入:一个一维整数数组A(其中元素可能是正数也可能是负数)
    输出:A的连续子数组,要求其和最大
    例如,输入数组是{-2, -5, 6, -2, -3, 1, 5, -6},其结果是{6, -2, -3, 1, 5},其和为7
    要求: (1) 算法时间复杂度为O(nlogn);(2) 写出算法伪代码;(3) 分析算法时间复杂度
(1)算法:
分治法,计算A[1...mid]的最大连续子数组A1[],再计算A[mid+1....n]的最大连续子数组A2[]。
  然后计算包含A[mid]和A[mid+1]的最大子数组,即找到形如A[i......mid]和A[mid+1...j]的最大连续数组,再将其合并
  (2)伪代码:
  FIND-CROSS-MAX(A,low,high,mid)
  left-sum = -∞
  sum = 0
  for i = mid downto low
  	sum = sum +A[i]
  	if sum > left-sum then
  		left-sum = sum
  		left-max = i
 right-sum = -∞
 sum = 0
 for i = mid+1 to high 
 	sum = sum + A[i]
 	if sum > right-sum then
 		right-sum = sum
 		right-max = i
 return  left-max,right-max,left-sum + right-sum
 
FIND-MAX(A,low,high)
if high == low then
	return(low,high,A[low]) 
mid = (high - low +1 ) / 2
(left-low,left-high,left-sum) = FIND-MAX(A,low,mid)
(right-low,right-high,right-sum) = FIND-MAX(A,mid+1,high)
(mid-low,mid-high,mid-sum) = FIND-CROSS-MAX(A,low,hign,mid)
max-sum = MAX(left-sum,right-sum,mid-sum)
if max-sum == left-sum then  return(left-low,left-high,left-sum)
if max-sum == mid-sum then return(mid-low,mid-high,mid-sum)
if max-sum == right-sum then return(right-low,right-high,right-sum)

递归方程:
T(n) = 1    n==1
T(n) = 2T(n/2) + O(n)
由master定理
T(n) = O(nlogn) 
  • 6、阅读https://oi-wiki.org/math/quick-pow/学习基于分治思想的“快速幂”算法。设计一个通过矩阵运算,在O(logN)时间内计算斐波那契数列第N项的算法。
    [ F ( n ) F ( n − 1 ) ] (1) \left[ \begin{matrix} F(n)\\ F(n-1) \\ \end{matrix} \right] \tag{1} [F(n)F(n1)](1)
    = A*
    [ F ( n − 1 ) F ( n − 2 ) ] (2) \left[ \begin{matrix} F(n-1)\\ F(n-2) \\ \end{matrix} \right] \tag{2} [F(n1)F(n2)](2)
    A =
    [ 1 1 1 0 ] (3) \left[ \begin{matrix} 1&1\\ 1&0\\ \end{matrix} \right] \tag{3} [1110](3)
    所以依次递归到F(2),F(1)
    共有n-2个矩阵A相乘,A^n-2
    根据快速幂运算 :如果n-2是偶数,An-2=An-2/2 * An-2/2
    如果n-2是奇数, = An-3/2 *An-3/2+1
    T(n) =T(n/2) = O(logn)
  • 7 、设计一个“三路归并”的排序算法,并分析它的时间复杂性。
每3个一组,共n/3组,每组比较3次。
类比二路归并。
即3叉树倒过来。
	T(n) = 0    n==1
	T(n) = 1     n==2
	T(n) = 3    n ==3
	T(n) = 3T(n/3) + O(n) -------最后一层
	T(n) = O(nlogn) 
  • 8、 逆序对数求解:有长度为N的浮点数组A,元素分别为a1, a2, …, aN。如果满足i<j且ai>aj,则(ai, aj)构成一个逆序对。设计分治方法求解数组A的逆序对个数,并分析算法的时间复杂性。
将数组分成两组,
前一半A[1:n/2],后一半A[n/2+1:n]分别求逆序对;
然后找前一半和后一半组成的逆序对。
在每次找逆序对时,将元素大小排序,得到新的B[1:n/2],C[1:n/2+1]
每次分别取前一半,后一半最小值B【1】,C【1】.
如果B【1】<C【1】,则前B【1】和后一半每个元素都能构成逆序对.取B中下一个数B【2】和C【1】接着比较;如果B【1】>C【1】,则取C的下一个数C【2】和B【1】进行比较,以此类推。
T(n)= O(1),n=1,2
T(n)= 	2T(n/2)+O(n)
T(n)=O(nlogn)
  • 7
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值