A[j]?
A[j-1]
sorted=false
endifi=i-1
endwhile
endBUBBLESORT
5.用两种方法证明logn!
=(nlogn)。
(1)用代数方法
(2)用积分方法
6.求解递推关系式f(n)=-6f(n-1)-9f(n-2)。
7.分治算法由哪些基本步骤组成?
分治算法的时间复杂性常满足什么样的递归方程,写出该方程的一般形式,并指出其中各参数的意义。
8.算法有哪五个特性?
9•什么是最优子结构?
10.贪心法与动态规划有何根本区别?
11.设计回溯算法通常包括哪些步骤?
12.随机算法分成那几类,各有什么特点?
三•算法填空
1.以下是计算xm的值的过程
power(x,m)
〃计算xm的值并返回。
ifm=0theny=
else
y=
y=y*yifm为奇数theny=x*yendif
endpower
2.以下是关于矩阵链乘的算法MATCHAIN1和MATCHAIN_PRODUCT
算法MATCHAIN1
输入:
矩阵链长度n,n个矩阵的阶r[1..n+1],其中r[1..n]为n个矩阵的行数,r[n+1]为第n个矩阵的列数。
输出:
n个矩阵相乘的数量乘法的最小次数,最优顺序的信息数组S[1..n,1..n]。
fori=1tonC[i,i]=0〃对角线d0置o
ford=1ton-1〃逐条计算对角线do~dn-1
fori=1ton-d//计算对角线dd的n-d个元素。
C[i,j]=gfork=i+1toj
x=
ifthen
C[i,j]=x;S[i,j]=kendif
endfor
endfor
returnC[1,n],S
endMATCHAIN1
根据S中的信息递归确定最优乘法顺序。
算法MATCHAIN_PRODUCT
输入:
矩阵链长度n,n个矩阵Mi,M2,…,Mn,最优乘法顺序的信息数组S[1..n,1..n]。
输出:
按最优顺序计算的矩阵链乘积M=MiM2…Mn。
M=matchain_product(1,n)
returnM
endMATCHAIN_PRODUCT
过程matchain_product(i,j)
//求按最优顺序计算的矩阵链乘积MiMi+i…Mj并返回。
ifthenreturnMi
else
A=matchain_product
B=matchain_product
C=multiply(A,B)//计算两个矩阵乘积C=AB。
returnC
endif
endmatchain_product
3.以下是迷宫问题的算法
算法MAZE
输入:
正整数m,n,表示迷宫的数组M[0..m+1,0..n+1](迷宫数据存于M[1..m,1..n]中),
迷宫的入口位置(ix,iy),出口位置(ox,oy)。
输出:
迷宫中入口至出口的一条通路,若无通路,则输出nosolution。
M[0,0..n+1]=M[m+1,0..n+1]=1
M[0..m+1,0]=M[0..m+1,n+1]=1//设置迷宫边界。
tag[1..m,1..n]=0
为dx[1..4],dy[1..4]置值
flag=false//flag表示是否存在通路。
x=ix;y=iy;tag[x,y]=1//进入入口,(x,y)表示当前位置。
i=1;k[i]=0
whileandnotflag
whileandnotflag
//试沿着下一个方向搜索。
x1=x+dx[k[i]];y1=y+dy[k[i]]
ifM[x1,y1]=0andtag[x1,y1]=0then//位置(x1,y1)可走
ifx1=oxandy仁oythen//找到一条通路
elsex=x1;y=y1;tag[x,y]=1//进入新位置。
i=i+1;k[i]=//准备搜索下一个位置。
endif
endif//否则,剪枝
endwhile
;x=x-dx[k[i]];y=y-dy[k[i]]〃回溯
endwhile
ifflagthen
outputroute(k,i)//输出由k[1..i]表示的通路。
elseoutputnosolution”//输出无解
endMAZE
4.下面是求一个序列的多数元素的递归算法
算法MAJORITY
输入:
n个元素的数组A[1..n]。
输出:
若A[1..n]存在多数元素,则输出;否则输出none。
c=candidate
(1)//求A[1..n]中的候选多数元素。
count=O//以下验证c是否为A[1..n]中的多数元素。
forj=1ton
ifA[j]=cthencount=count+1
endfor
ifthenreturnc
elsereturnnone
endMAJORITY
过程candidate(m)
//求A[m..n]中的候选多数元素并返回。
j=m;c=A[m];count=1
whilej0
j=j+1
ifthen
else//除去两个不同的元素。
endwhile
ifj=nthenreturnc//此时序列已扫描完毕。
elsereturn
endcandidate
5.下面是0-1背包问题的算法
算法KNAPSACKREC
输入:
物品数n,n种物品的体积数组s[1..n和价值数组v[1..n],
背包容量C。
输出:
装入背包物品的最大总价值,以及最优解的信息数组H[0..n,0..C。
fori=0ton
forj=0toC
V[i,j]=-1
maxv=knapsack(n,C)
returnmaxv,H
endKNAPSACKREC
过程knapsack(i,j)
//从i种物品中选择物品装入容量为j的背包中,求背包中
//物品所能达到的最大总价值并返回,同时将最优解的相应
〃信息存入数组H[0..n,0..C]。
ifV[i,j]=-1then〃相应的子问题未解过。
ifi=0orj=0thenV[i,j]=O
else
ifs[i]>jthen
V[i,j]=
H[i,j]=
else
a1=
a2=knapsack(i-1,j)
ifa1>=a2then
V[i,j]=a1;H[i,j]=
else
V[i,j]=a2;H[i,j]=0
endif
endif
endif
endif
return
endknapsack
算法KNAPSACK_SOLUTION
输入:
物品数n,背包容量C,n种物品的体积数组s[1..n],相应的0/1背包问题的
最优解信息数组H[0..n,0..C]。
输出:
相应的0/1背包问题的最优解X[1..n]。
y=C//y表示当前背包的剩余容量
X[n]=H[n,C]
fori=nto2
ifX[i]=1theny=
X[i-1]=
endfor
returnX
endKNAPSACK_SOLUTION
6.以下是随机化的线性选择算法
算法RANDOMIZEDSELECT
输入:
整数n,k,1<=k<=n,以及n个元素的数组A[1..n]。
输出:
A中的第k小元素s。
s=rselect(A,1,n,k)
endRANDOMIZEDSELECT
过程rselect(A,low,high,k)
〃求A[low..high]中的第k小元素并返回。
v=random(low,high)
mm=
将A[low..high]分成三个数组:
Ai={a|amm}
//以下选择一个子问题递归或直接解。
case
|Ai|>=k:
return//求Ai的第k小元素。
|Ai|+|A2|>=k:
returnmm
//此时mm为A[low..high]的第k小元素。
|Ai|+|A2|returnrselect
〃求A3的第k-|A1|-|A2|小元素
endcase
endrselect
7.以下是归并排序的分治算法
MERGESORT
输入:
n个元素的数组A[1..n]。
输出:
按非降序排序的数组A[1..n]。
mergesort(A,1,n)
endMERGESORT
过程mergersort(A,low,high)
〃将A[low..high]按非降序归并排序。
iflowmid=//平均分解成两个子数组
mergesort〃分别对两个子数组递归
mergesort
MERGE(A,low,mid,high)//合并两个有序的子数组
endif
endmergesort
8.以下是分数背包问题的贪心算法
算法GREEDY_KANPSACK
输入:
表示背包容量的实数C,物品数n,表示n个物品的体积和价值的实数数组
s[1..n]和v[1..n]。
输出:
装入背包物品的最大总价值maxv和相应的最优解x[1..n]。
fori=1ton
y[i]=v[i]/s[i]〃求n个物品的单位体积价值y[1..n]。
endfor
//对y[1..n]按降序地址排序,排序结果返回到数组d[1..n]//中,使得
y[d[1]]>=y[d[2]]>=->=y[d[n]]。
d=sort(y,n)
fori=1tonx[i]=0〃对x[1..n]初始化。
i=1;maxv=0;rc=C//以下rc表示当前背包的剩余容量。
whileandi<=n
j=d[i]//uj为当前考虑选择的物品
ifs[j]<=rcthen
x[j]=;rc=〃物品uj全部装入背包。
else
x[j]=〃选择物品uj的一部分将背包填满。
rc=0
endif
maxv=
i=
endwhile
returnmaxv,x[1..n]
endGREEDY_KNAPSACK
9.以下是子集合问题的回溯算法
算法SUBSETSUM
输入:
正整数n,正实数b,表示含有n个正实数的集合S的数组a[1..n]。
输出:
集合S的元素之和等于b的所有子集,若无解,则输出"nosolution”。
fori=1tonx[i]=0//用x[1..n]表示子集,初始化为空集。
r=0
fori=1tonr=r+a[i]
flag=subset(O,0,r)
ifnotflagthenoutputnosolution”
endSUBSETSUM
过程:
subset(k,sum,r)
〃在已经求出的部分解x[1..k]固定的情况下,求关于S,b的//子集和问题的所有
解并输出,有解返回true,否则返回//false。
k
n
//sum=ajXj表示当
前子集的兀素之和,r=3i
表示剩
i1
ik1
//余兀素之和。
ifsum=bthen//找到一个解。
outputx[1..n];t=true
//输出当前解。
else
ifk=bthen//可继续往下搜索。
r=
x[k+1]=0
t仁subset//搜索左子树(对应x[k+1]=0)
x[k+1]=
t2=setsub//搜索右子树
t=t1ort2
else
t=
endif
endifreturnt
endsubset
10.下面是一个求n个元素全排列的算法
算法PERMUTATION
输入:
正整数n和存储n个元素a1,a2,…,an的数组P[1..n]输出:
a1,a2,…,an的全排列。
perml
(1)
过程perml(m)
//在P[1..m-1]中元素固定不变的情况下,求P[m..n]中元素
//的全排列,并输出P[仁n]中元素的相应排列。
ifthen//此时只求一个元素P[n]的全排列。
outputP[仁n];//输出P[1..n]中元素的一个新排列。
else
forj=mton
P[m]?
P[j]
endfor
endif
endperml
11.以下是求最长公共子序列的算法
算法LCS
输入:
非负整数n、m,长度分别为n和m的序列A=玄但2…an和B=b1b2…bn。
输出:
A和B的最长公共子序列长度L[n,m]和存储最长公共子序列的有关信息的数
组H[1..n,1..m]。
fori=0tonL[i,0]=0
forj=0tomL[0,j]=0
fori=1ton
forj=1tom
ifai=bjthen
L[i,j]=;H[i,j]=0
else
ifL[i-1,j]>=L[i,j-1]then
L[i,j]=;H[i,j]=1
else
L[i,j]=L[i,j-1];H[i,j]=
endif
endif
endfor
endfor
returnL[n,m],H
endLCS
算法LCSS
输入:
非负整数n、m,长度分别为n和m的序列A和B的最长公共子序列的有
关信息数组H[1..n,1..m],序列A=a132…an。
输出:
序列A和B的最长公共子序列C。
C=Q//①表示空序列。
i=n;j=m
whilei>0andj>0
ifH[i,j]=0then
在C的前面插入ai
i=;j=
else
ifH[i,j]=theni=i-1elsej=j-1
endif
endwhile
returnC
endLCSS
12.以下是0-1背包问题的回溯算法
算法KNAPSACK01
输入:
物品数n,n种物品的体积数组s[1..n]和价值数组v1..n],背包容量C。
输出:
装入背包物品的最大总价值maxv和最优解x0[1..n]。
〃用x[1..n]表示选择的物品子集,初始化为空集。
rv=0;rs=0
fori=1ton
rv=rv+v[i];rs=rs+s[i]
maxv=0;x0[1..n]=x[1..n]=0//x0表示当前最优解。
knapsack01(0,C,0,rv,rs)
outputmaxv,x0[1..n]
endKNAPSACK01
过程:
knapsack(k,r,cv,rv,rs)
〃在已知当前最优值为maxv且已经求出的部分解x[1..k]固
//定的情况下,求0/1背包问题的最优值和最优解,并更新
//maxv和x0。
r表示当前背包的剩余容量,cv表示当前背
n
n
//包中物品的总价值为,rv=Vj
rs=耳。
ik1
ik1
ifr>=0andcv>maxvthen//找到更优的解,更新当前最优值。
maxv=;x0=x
endif
ifrs<=rthen
ifcv+rv>maxvthenmaxv=cv+rv;x0[1..k]=x[1..k];x0[k+1..n]=1;
endif
else
ifr>0andcv+rv>maxvthen//可继续往下搜索。
rv=;rs=
x[k+1]=0
knapsack//搜索左子树(对应x[k+1]=0)
x[k+1]=1
knapsack//搜索右子树
endif
endknapsack
13.下面是求第k小兀素的算法
算法SELECT
输入:
整数n,k,1<=k<=n,以及n个元素的数组A[1..n]
输出:
A中的第k小元素s。
s=select(A,1,n,k)
endSELECT
中的第k小元素并返回。
为当前处理的元素个数。
当元素个数<44时,直接求解。
排序
过程select(A,low,high,k)
//求A[low..high]p=high-low+1〃p
ifp<44then//将A[low..high]return(A[low+k-1])endif
//以下分解子问题。
q=p/5;//将A[low..high]每5个分组,剩余的被排除。
将A[low..low+5q-1]分为q组,每组5个元素;
将q组中的每一组单独排序并找出中项,所有的中项存于数组M[1..q]中;
mm=select()//求中项序列M的中项mm
将A[low..high]分成三组:
A={a|amm}
//以下选择一个子问题递归或直接解。
case
|A1|>=k:
returnselect()
|A1|+|A2|>=k:
returnmm
|A1|+|A2|returnselect()
endcase
endselect
14.有期限的作业安排问题
算法JOB_ARRANGEMENT
输入:
作业数n,表示n个作业的期限和收益的数组d[1..n]和p[1..n]。
输出:
最优的作业安排序列J[1..k]和安排的作业数k。
〃对p[1..n]按降序地址排序,排序结果返回到数组a[1..n]
//中,使得p[a[1]]>=p[a[2]]>=•->=p[a[n]]。
a=sort(p,n)
d[0]=0;J[0]=0〃设一个虚拟作业编号为0,期限为0
J[1]=a[1]//直接安排收益最高的作业ja[i]
k=1//以下k总是表示当前已被安排的作业数。
fort=2ton
i=//ji为当前考虑安排的作业。
r=k
whileandd[J[r]]>r
ifd[J[r]]<=d[i]andthen
//把i插入到J中的r+1位置上。
fors=ktor+1J[s+1]=J[s]
J[r+1]=
k=
endif
endfor
returnk,J[1..k]
endJOB_ARRANGEMENT
15.以下是关于矩阵链乘的算法MATCHAIN1和MATCHAIN_PRODUCT
算法MATCHAIN1
输入:
矩阵链长度n,n个矩阵的阶r[1..n+1],其中r[1..n]为n个矩阵的行数,r[n+1]
为第n个矩阵的列数。
输出:
n个矩阵相乘的数量乘法的最小次数,最优顺序的信息数组S[1..n,1..n]。
fori=1tonC[i,i]=0〃对角线do置o
ford=1ton-1〃逐条计算对角线d0~dn-1
fori=1ton-d//计算对角线dd的n-d个元素。
C[i,j]=g
fork=i+1toj
x=
ifthen
C[i,j]=x;S[i,j]=k
endif
endfor
endfor
endfor
returnC[1,n],S
endMATCHAIN1
根据S中的信息递归确定最优乘法顺序。
算法MATCHAIN_PRODUCT
输入:
矩阵链长度n,n个矩阵M1,M2,…,Mn,最优乘法顺序的信息数组S[1..n,1..n]。
输出:
按最优顺序计算的矩阵链乘积M=M1M2…Mn。
M=matchain_product(1,n)
returnM
endMATCHAIN_PRODUCT
过程matchain_product(i,j)
//求按最优顺序计算的矩阵链乘积MiMi+1…Mj并返回。
ifthenreturnMi
else
A=matchain_product
B=matchain_product
C=multiply(A,B)//计算两个矩阵乘积C=AB。
returnC
endif
endmatchain_product
四•算法设计与分析
1.格雷码问题
对于给定的正整数n,格雷码为满足如下条件的一个编码序列:
(1)序列由2个编码组成,每个编码都是长度为n的二进制位串。
(2)序列中无相同的编码。
(3)序列中位置相邻的两个编码恰有一位不同。
例如:
n=2时的格雷码为:
{00,01,11,10}。
设计求格雷码的递归算法。
2.给出一个分治算法,在一个具有n个数的数组中找出第二个最大元素。