算法课 是这学期开的,但几乎没有去过 ,最近开始自己看自己学。
先从分治递归开始
引用 算法老师课件里的东西:
凡治众如治寡,分数是也。
—《孙子兵法》
大概意思就是:治理大军团就象治理小部队一样有效,是依靠合理的组织、结构、编制。
将一个难以直接解决的大问题,合理分割成一些规模较小的相同问题,以便各个击破,这个策略就叫分而治之
(分治法)。
主要思想是:将一个问题不断分割成若干个小问题,然后通过对小问题的求解再生成大问题的解。
分治法可以分为两个重要步骤:
(1)自顶向下:将问题不断分割成小的问题。
(2)自底而上:将小问题解决来构建大问题的解。
分治和递归经常同时应用在算法设计中。
例子1:找一组数的最大最小数。
直接求解:需要 n - 1 次比较来求最小数(n 个数两两比较共需要 n – 1 次),然后另外 n - 2 次比较来求最大
数(除去最小值的剩下 n – 1 个数两两比较共需要 n – 2 次)。总计为( 2n - 3)次。
分治方法:
(1)将数据集 S 均分为 S1 和 S2;
(2)求解 S1 和 S2 中的最大和最小值;
(3)最终的最大和最小值可以计算得到:min( S1, S2 ), max( S1, S2 );
(4)采用同样的处理方法递归处理 S1 和 S2。
算法描述(为方便起见,假定 n 是 2 的指数倍,n > 1):
min_max(S, min, max) {
if |S| = 2 then
min min(S)
max max(S)
else
split S evenly into S1 and S2
min_max(S1, min1, max1)
min_max(S2, min2, max2)
min min(min1, min2)
max max(max1, max2)
}
代码如下:
#include <iostream>
using namespace std;
void min_max(int *a,int start,int end,int *min,int *max);
int main(void)
{
int a[8]={2,3,6,5,1,2,8,3};
int min=a[0];
int max=a[0];
min_max(a,0,7,&min,&max);
for(int i=0;i<8;i++){
cout<<a[i];
}
cout<<endl;
cout<<"max="<<max<<endl;
cout<<"min="<<min<<endl;
}
void min_max(int *a,int start,int end,int *min,int *max)
{
if((end-start)<=1){
if(a[start] > a[end]){
*max = *max > a[start] ? *max : a[start];
*min = *min < a[end] ? *min : a[end];
}else{
*max = *max > a[end] ? *max : a[end];
*min = *min < a[start] ? *min : a[start];
}
return;
}else{
int i = start + (end-start)/2;
min_max(a,start,i,min,max);
min_max(a,i+1,end,min,max);
}
}
例子2:排列问题。
设计一个递归算法生成 n 个元素{ r1, r2, …, rn }的全排列。
全排列:从 n 个不同元素中任取m(m ≤ n)个元素,按照一定的顺序排列起来,叫做从 n 个不同元素中取出 m 个
元素的一个排列。当 m = n 时所有的排列情况叫全排列。
如 1, 2, 3 三个元素的全排列为:
1, 2, 3 1, 3, 2
2, 1, 3 2, 3, 1
3, 1, 2 3, 2, 1
共 3!= 3 * 2 * 1 = 6 种排列
全排列的递归算法设计
从实际例子中观察可知:
(1){ 1 }的全排列为:1
(2){ 1, 2 }的全排列为:1, 2 2, 1
(3){ 1, 2, 3 }的全排列为:
1, 2, 3 1, 3, 2 → { 2, 3 } 的全排列加上 1 所得
2, 1, 3 2, 3, 1 → { 1, 3 } 的全排列加上 2 所得
3, 1, 2 3, 2, 1 → { 1, 2 } 的全排列加上 3 所得
即:3 个元素的全排列问题可转化为求 2 个元素的全排列问题。
………………….
推广结论:n 个元素的全排列问题可转化为求 n - 1 个元素的全排列问题(递归设计)。
全排列的递归算法
设 R ={ r1, r2, …, rn }是要进行排列的 n 个元素,Ri = R - { ri }。
集合 X 中元素的全排列记为 perm( X )。
( ri )perm( X ) 表示在全排列 perm( X ) 的每一个排列前加上前缀得到的排列。R的全排列可归纳定义如下:
当 n = 1 时,perm( R ) = ( r ),其中 r 是集合 R 中唯一的元素;
当 n > 1 时,perm( R ) 由 ( r1 )perm(R1),( r2 )perm(R2),…,( rn )perm(Rn)构成。
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
void Swap(char *text1,char *text2)
{
char temp = *text1;
*text1 = *text2;
*text2 = temp;
}
void AllRange(char *text,int s,int e)
{
if(s == e){
static int s_i = 1;
cout<<"the "<<s_i++<<"AllRange "<<text<<endl;
}else{
for(int i=s;i<=e;i++){
Swap(text+s,text+i);
AllRange(text,s+1,e);
Swap(text+s,text+i);
}
}
}
void Foo(char *text)
{
AllRange(text,0,strlen(text)-1);
}
int main(void)
{
char text[] = "123";
cout<<text<<endl;
Foo(text);
return 0;
}