一、题目描述
找出n个自然数(1,2,3……n)中取r个数的组合。例如:当n=5,r=3时,所有组合为;
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
total=10{组合的总数}
二、循环算法思路
分析以上的例子可得每组中的3个数有两个特点:(1)互不相同;(2)前面的数小于后面的数。因此,当r=3时,可用三重循环模拟每个组合中的3个数,当满足以上讨论的两个特点时,就得到一定的组合。
三、循环算法的伪码描述
main1()
{
int n=5,i,j,k,t;
t=0;
for(i=1;i<=n;i++) {
for(i=1;i<=n;i++) {
for(i=1;i<=n;i++) {
if((i<j)and(j<k)) {
t=t+1;
print(i,j,k);
}
}
}
}
print(t);
}
或:
main2()
{
int n=5,r=3,i,j,k,t;
t=0;
for(i=1;i<=n-r+1;i++) {
for(i=1;i<=n-r+2;i++) {
for(i=1;i<=n-r+3;i++) {
t=t+1;
print(i,j,k);
}
}
}
print(t);
}
四、递归算法思路
在循环算法的思路中,对于以上这个事例来说,每个组合中的数据从大到小和从小到大排列都可以设计出相应的算法。但递归思路进行设计时,每个组合数中的数据从大到小排列是必须的,因为递归算法设计是要找出大规模的问题与小规模的问题之间的关系。
n=5,r=3时,从大到小排列的组合数为:
5 4 3
5 4 2
5 4 1
5 3 2
5 3 1
5 2 1
4 3 2
4 3 1
4 2 1
3 2 1
total=10{组合的总数}
分析以上数据,得到的组合数的规律为:
(1)固定第一个数5,其后就是求解n=4,r=2的组合数,共6个组合
(2)固定第一个数4,其后就是求解n=3,r=2的组合数,共3个组合
(3)固定第一个数3,其后就是求解n=2,r=2的组合数,共1个组合
这就找到了“5个数中的3个数的组合”与“4个数中的2个数的组合、3个数中的2个数的组合、2个数中的2个数的组合”的递归关系。
一般的,递归算法分为两个步骤:
(1)n个数中r个数组合递推到“n-1个数中r-1个数有组合,n-2个数中r-1个数有组合,n-3个数中r-1个数有组合……r-1个数中r-1个数有组合”,共n-r+1次递归
(2)递归停止的条件为r=1;
五、递归算法的伪码描述
int a[100];
comb(int m,int k)
{
int i,j;
for(i =m;i>=k;i--) {
a[k]=i;
if(k>1) {
comb(i-1,k-1);
} else {
for(j=a[0];j>0;j--) {
print(a[j]);
}
print(\n);
}
}
}
main3()
{
int n,r;
print("n,r=");
input(n,r);
if(r>n) {
print("input n,r error!");
} else {
a[0]=r;
comb(n,r);
}
}
六、循环算法 pk 递归算法
循环算法分析:
循环算法的两个算法中,前者穷举了所有可能的情形,从中选出符合条件的解;后者则直接按组合中三个数据的特点,确定了相应的循环范围。后者的算法效率更高,但是这两个算法的复杂性均为O(n3)。显然,当n较大时,算法的效率低下。
递归算法分析:
递归算法的递归深度是r,每个算法要递归m-k+1次,所以时间复杂度为O(r*n)。递归的层次是可以控制的,而循环的层次只能是固定的。而且,递归不需要自己构造“循环不变式”,而只需找出递归关系和最小问题的解。