- 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(n−1)](1)
= A*
[ F ( n − 1 ) F ( n − 2 ) ] (2) \left[ \begin{matrix} F(n-1)\\ F(n-2) \\ \end{matrix} \right] \tag{2} [F(n−1)F(n−2)](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)