递归概念:直接或者间接调用自身的算法称为递归算法。
递归函数:用函数自身给出定义的函数称为递归函数。
分治法的基本思想:将一个规模为n的问题分解为k个规模较小的子问题,这些子问题是相互独立些与原问题相同。
递归地解决这些子问题,然后将各子问题的解合并得到原问题的解。
1.阶乘函数
n!={1 n=0
n(n-1)! n>0
public static int factorial(int n)//参数n为非负数
{ if(n==0) return 1;
return n*factorial(n-1);
}
2.Fibonacci数列
数列1,1,2,3,5,8,13,21,34,55,称为Fibonacci数列
F(n)={1 n=0,1
F(n-1)+F(n-2) n>1
public static int fibonacci(int n)
{if(n<=1){return 1;}
return fibonacci(n-1)+fibonacci(n-2)
}
3.排列问题
我们先分析一下全排列算法是如何实现的
以list[3]={1,2,3}为例。
一、先是123,然后1与1自己对换,递归排列23,2与2自己对换,递归排列3,再递归时满足start==length,即越界,所以把123打印出来;
然后上一步2与2自己对换后,2与3对换,(暂时是132),递归到2与2对换,再递归满足start==length,打印132;
二、最先一步1与1自己对换后,1与2对换,(暂时是213),递归排列13,1与1自己对换,递归排列3. 3与3自己对换,然后满足If条件打印213:
然后退一步,1与3对换,(暂时是231),递归到1与1自己对换,再递归满足打印条件,打印231;
三、1与1、2对换后,1最后与3对换,(暂时是321),递归排列21,2与2对换,递归排列1.1与1自己对换,后来满足打印条件打印321. 2再与1对换,再递归到2与2自己对换,后来打印出312.
附带程序:
public static void perm(Object[] list,int k,int m)
{
if(k==m)
{ for(int i=0;i<=m;i++){
System.out.print(list[i]);
}
System.out.println();
}
else{
for(int i=k;i<=m;i++)
{MyMath.swap(list,k,i);
perm(list,k+1,m);
MyMath.swap(list,k,i);
}
}
}
4.Hanoi塔问题
汉诺(Hanoi)塔问题:古代有一个梵塔,塔内有三个座A、B、C,A座上有64个盘子,盘子大小不等,大的在下,小的在上(如图)。有一个和尚想把这64个盘子从A座移到B座,但每次只能允许移动一个盘子,并且在移动过程中,3个座上的盘子始终保持大盘在下,小盘在上。在移动过程中可以利用B座,要求打印移动的步骤。如果只有一个盘子,则不需要利用B座,直接将盘子从A移动到C。
- 如果有2个盘子,可以先将盘子1上的盘子2移动到B;将盘子1移动到c;将盘子2移动到c。这说明了:可以借助B将2个盘子从A移动到C,当然,也可以借助C将2个盘子从A移动到B。
- 如果有3个盘子,那么根据2个盘子的结论,可以借助c将盘子1上的两个盘子从A移动到B;将盘子1从A移动到C,A变成空座;借助A座,将B上的两个盘子移动到C。这说明:可以借助一个空座,将3个盘子从一个座移动到另一个。
- 如果有4个盘子,那么首先借助空座C,将盘子1上的三个盘子从A移动到B;将盘子1移动到C,A变成空座;借助空座A,将B座上的三个盘子移动到C。
public static void hanoi(int n,int a,int b,int c){
if(n>0){
hanoi(n-1,a,c,b);//表示将n-1个圆盘从a移动到c,借助于b盘
move(a,b);//表示将塔座a上的圆盘移动到塔座b上
hanoi(n-1,c,b,a);
}
}
5.二分搜索递归表示
public static int binarySearch(int [] a,int x,int n){
int left=0;int right=n-1;
while(left<=right)
{int middle=(left+right)/2;
if(x==a[middle])return middle;
if(x>a[middle])left=middle;
else{right==middle-1}
}
return -1;//未找到x
}
6.合并排序(未实现)
public static void mergeSort(int a[],int left,int right)
{
if(left<right){//至少有两个元素
int i=(left+right)/2;
mergeSort(a,left,i);
mergeSort(a,i+1,right);
merge(a,b,left,i,right);//合并到数组b
copy(a,b,left,right);//复制回数组a
}
}
7.快速排序
private static void qSort(int p,int r)//q为第一个元素下标,r为最后一个元素下标
{ if(p<r){
int q=partitiion(p,r);
qSort(p,q-1);//
qSort(q+1,r);
}
}
private static int partition(int p,int r)
{ int i=p;
int j=r+1;
int x=a[p];
while(true){
while(a[++i].compareTo(x)<0 && i<r);//从第二个元素找(++i先加后计算)找到比第一个元素大的元素,i为其下标
while(a[++j].compareTo(x)>0);从最后一个元素开始找,找到比第一元素小的,j为其下标。
if(i>=j) break;
MyMath.swap(a,i,j);
}
a[p]=a[j];//将j下标的元素放到第一个位置上。
a[j]=x;//将原来第一个元素放到,它排完序的具体位置。
return j;//返回第一个元素具体在哪个位置的下标。
}
算法改进
可以在a集合中随机产生一个,而不是将第一个元素作为要排的第一个元素。
private static int randomizedPartition(int p,int r)
{ int i=random(p,r);
MyMath.swap(a,i,p);
return partition(p,r);
}
7.线性时间选择
问题描述:给定线性序集中n个元素和一个整数k,k>=1并且k<=n,要求找出n个元素中第k小的元素。
private static Comparable randomizedSelect(int p,int r,int k)
{ if(p==r) return a[p];//找到了第k小元素
int i=randomizedpartition(p,r);//排好了第i位置的元素。(即当前第i个位置就放的是排好序的那个元素)。
int j=i-p+1;//左区域的元素个数。
if(k<=j){
return randomizedSelect(p,i,k);//去左边区域找。
}
else{
return randomizedSelect(i+1,r,k-j);//(k-j)为n个元素中第k小元素在右边区域是第几小,则为k-j。
}
}
8.最接近点对问题
问题描述:找距离最近的两个点
以下算法未实现
public static double cpair1(s)
{ n=|s|;
if(n<2) return ∞;
d1=cpairl(s1);
d2=cpairl(s2);
p=max(s1);
q=min(s2);
d=min(d1,d2,q-p);
return d;
}