转载于
https://www.cnblogs.com/ysocean/p/8005694.html
并总结。。。
递归的定义
递归,就是在运行的过程中调用自己。
递归必须要有三个要素:
①、边界条件
②、递归前进段
③、递归返回段
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
一个阶乘递归的例子
public static int getFactorial(int n){
if(n >= 0){
if(n==0){ //1
System.out.println(n+"!=1");
return 1; //2
}else{
System.out.println(n);
int temp = n*getFactorial(n-1);//3
System.out.println(n+"!="+temp);
return temp;//4
}
}
return -1;
}
程序解释:
//1 是边界条件,当他不满足的时候,递归前进 即//3,
直到递归到条件满足的时候,此时的情况是
要算出getFactorial(4)(简写为g(4));递归为:
g(4)的temp = 4*g(3);
g(3)的temp = 3*g(2);
g(2)的temp = 2*g(1);
g(1)的temp = 1*g(0);
g(0)返回1;
那么g(1)返回他的temp,也就是1;
g(2)返回!2;g(3)返回!3;
g(4)返回!4;结束递归
递归就是,一层层递归包住(调用自身),直到边界条件,再利用return一层层的撕开,最后得到结果
递归的二分查找
注意:二分查找的数组一定是有序的!!!
public static int search(int[] array,int key,int low,int high){
int mid = (high-low)/2+low;
if(key == array[mid]){//查找值等于当前值,返回数组下标
return mid;
}else if(low > high){//找不到查找值,返回-1
return -1;
}else{
if(key < array[mid]){//查找值比当前值小
return search(array,key,low,mid-1);
}
if(key > array[mid]){//查找值比当前值大
return search(array,key,mid+1,high);
}
}
return -1;
}
递归的二分查找和非递归的二分查找效率都为O(logN),递归的二分查找更加简洁,便于理解,但是速度会比非递归的慢。
归并排序
归并算法的中心是归并两个已经有序的数组。归并两个有序数组A和B,就生成了第三个有序数组C。数组C包含数组A和B的所有数据项。
非递归算法
public static int[] sort(int[] A,int[] B){
int[] C = new int[A.length+B.length];
int numA=0,numB=0,numC=0;
while( numbA<A.length && numbB<B.length){
if( A[numA]<B[numB])
C[numC++]=A[numA++];
else
C[numC++]=B[numB++];
}
while( numA=A.length && numbB<B.length){
C[numC++]=B[numB++];
}
while(bNum == b.length && aNum < a.length){
c[cNum++] = a[aNum++];
}
return c;
递归算法
归并排序的思想是把一个数组分成两半,排序每一半,然后用上面的sort()方法将数组的两半归并成为一个有序的数组。如何来为每一部分排序呢?这里我们利用递归的思想:
把每一半都分为四分之一,对每个四分之一进行排序,然后把它们归并成一个有序的一半。类似的,如何给每个四分之一数组排序呢?把每个四分之一分成八分之一,对每个八分之一进行排序,以此类推,反复的分割数组,直到得到的子数组是一个数据项,那这就是这个递归算法的边界值,也就是假定一个数据项的元素是有序的。
public static int[] mergesort(int[] array,int start, int last){
// start和last为下标;
if( start<last){ //边界条件
int mid = (start+last)/2;
mergesort(array,start,mid);//递归
mergesort(array,mid+1,last);//递归
merge(array,start,mid,last);//合并
}
return array;
}
public static void merge(int[] array,int start , int mid, int last){
int leftnum = start;
int rightnum = mid+1;
int[] temp = new int[last-start+1];
int k = 0;
while(leftnum<=mid && rightnum<=last){
if(array[left]<array[right])
temp[k++] = array[left++];
else
temp[k++] = array[right++];
}
//把左边剩余数组元素移入新数组中
while(left <= mid){
temp[k++] = array[left++];
}
//把右边剩余数组元素移入到新数组中
while(right <= last){
temp[k++] = c[right++];
}
//把新数组中的数覆盖到c数组中
for(int k2 = 0 ; k2 < temp.length ; k2++){
array[k2+start] = temp[k2];
}
}
此递归为,满足条件递归,不满足
当start=last的时候,即
要排序比较的左边和右边只有一个数,
直接两次return(array),然后调用
merge,此时即左边右边只有一个数,递归到底了
比较后组成长度为2的有序数组。。。
消除递归
一个算法作为一个递归的方法通常通概念上很容易理解,但是递归的使用在方法的调用和返回都会有额外的开销,通常情况下,用递归能实现的,用循环都可以实现,而且循环的效率会更高,所以在实际应用中,把递归的算法转换为非递归的算法是非常有用的。这种转换通常会使用到栈。
背包问题
背包问题也是计算机中的经典问题。在最简单的形式中,包括试图将不同重量的数据项放到背包中,以使得背包最后达到指定的总重量。
比如:假设想要让背包精确地承重20磅,并且有 5 个可以放入的数据项,它们的重量分别是 11 磅,8 磅,7 磅,6 磅,5 磅。这个问题可能对于人类来说很简单,我们大概就可以计算出 8 磅+ 7 磅 + 5 磅 = 20 磅。但是如果让计算机来解决这个问题,就需要给计算机设定详细的指令了。
算法如下:
一、如果在这个过程的任何时刻,选择的数据项的总和符合目标重量,那么工作便完成了。
二、从选择的第一个数据项开始,剩余的数据项的加和必须符合背包的目标重量减去第一个数据项的重量,这是一个新的目标重量。
三、逐个的试每种剩余数据项组合的可能性,但是注意不要去试所有的组合,因为只要数据项的和大于目标重量的时候,就停止添加数据。
四、如果没有合适的组合,放弃第一个数据项,并且从第二个数据项开始再重复一遍整个过程。
五、继续从第三个数据项开始,如此下去直到你已经试验了所有的组合,这时才知道有没有解决方案。
public class Knapsack {
private int[] weights; //可供选择的重量
private boolean[] selects; //记录是否被选择
public Knapsack(int[] weights){
this.weights = weights;
selects = new boolean[weights.length];
}
/**
* 找出符合承重重量的组合
* @param total 总重量
* @param index 可供选择的重量下标
*/
public void knapsack(int total,int index){
if(total < 0 || total > 0 && index >= weights.length){
return;//没找到解决办法,直接返回
}
if(total == 0){//总重量为0,则找到解决办法了 //0
for(int i = 0 ; i < index ; i++){
if(selects[i] == true){
System.out.println(weights[i]+" ");
}
}
System.out.println();
return; //1
}
selects[index] = true; //2
knapsack(total-weights[index], index+1);//3
selects[index] = false;//4
knapsack(total, index+1);//5
}
public static void main(String[] args) {
int array[] = {11,9,7,6,5};
int total = 20;
Knapsack k = new Knapsack(array);
k.knapsack(total, 0);
}
}
程序解释:
以测试用例为例子:
第一次调用ks(20,0);
s[0]=T;然后递归
调用ks(9,1);
s[1]=T;然后递归
调用ks(0,2);
这时触发边界条件0
输出了一个结果后
return
注意:此时是ks[0,2]的return,返回到了
ks[9,1]那次调用的//3那行,也就是此时index=1;
继续//4(即寻找下一个方案)
s[1]=false,。。。
组合问题
背包问题与组合问题基本完全一样。。。
在数学中,组合是对事物的一种选择,而不考虑他们的顺序。
比如有5个登山队员,名称为 A,B,C,D和E。想要从这五个队员中选择三个队员去登峰,这时候如何列出所有的队员组合。(不考虑顺序)
public class Combination {
private char[] persons;//组中所有可供选择的人员
private boolean[] selects;//标记成员是否被选中,选中为true
public Combination(char[] persons){
this.persons = persons;
selects = new boolean[persons.length];
}
public void showTeams(int teamNumber){
combination(teamNumber,0);
}
/**
*
* @param teamNumber 需要选择的队员数
* @param index 从第几个队员开始选择
*/
public void combination(int teamNumber,int index){
if(teamNumber == 0){//当teamNumber=0时,找到一组
for(int i = 0 ; i < selects.length ; i++){
if(selects[i] == true){
System.out.print(persons[i]+" ");
}
}
System.out.println();
return;
}
//index超过组中人员总数,表示未找到
if(index >= persons.length ){
return;
}
selects[index] = true;
combination(teamNumber-1, index+1);
selects[index] = false;
combination(teamNumber, index+1);
}
public static void main(String[] args) {
char[] persons = {'A','B','C','D','E'};
Combination cb = new Combination(persons);
cb.showTeams(3);
}
}