理论上,任何循环都可以写为递归的形式
二者均存在着缺陷
1.递归的层层深入,可能会造成栈溢出的情况
2.存在着没有循环的语言,只能使用递归
本文章我们主要学习递归
eg1.打印0-9
public class Main {
public static void print(int i){
System.out.println(i);
i++;
if(i == 10)
return ;
print(i);
}
public static void main(String[] args) {
print(0);
}
}
eg2.实现递归打印0-n
public class Main {
public static void print(int n){
if(n>0)
print(n-1);
System.out.println(n);
}
public static void main(String[] args) {
print(7);
}
}
不难总结,无参函数递归是一个死循环,而递归的实现依靠参数值的变化来实现,当设置的递归无法实现,往往是设置的参数不合理
eg3.实现递归打印i-n
算法二中如果我们想先打印,再递归,由于执行起点为常数,无法实现一个参数的改变,因此,我们重新设置参数来调整算法
public static void print(int begin,int end) {
System.out.println(begin++);
if(begin>end)
return;
print(begin,end);
}
因此可以得到,递归的设计是灵活的,当发现循环的步骤中存在相似性(数学上的递推关系),通过改变传入参数的个数和类型来实现递归,除此以外,还要根据问题实现合理的终止条件
入门·练习
练习1,递归实现阶乘
public static int mul(int n){
if(n>1)
return n*mul(n-1);
else
return 1;
}
练习2,递归实现1+...+n
public static int sum(int n){
if(n>1)
return n+sum(n-1);
else
return 1;
}
练习3,递归实现1-2+3-4+5-6+...n
public static double cal(int n){
if(n>1)
return Math.pow(-1,n-1)*n+cal(n-1);
else
return 1;
}
练习4,递归实现数组元素累加
public static int addAll(int[] arr,int n){
if(n>0)
return arr[n-1]+addAll(arr,n-1);
else
return 0;
}
练习5,递归实现数组元素从i累加到最后一个元素
public static int addAll(int[] arr,int i){
if(i< arr.length)
return arr[i]+addAll(arr,i+1);
else
return 0;
}
练习6,递归实现字符串比较功能,即equals()
public static boolean judgeStr(String sa,String sb){
if(sa.length()!=sb.length())
return false;
if(sa.length() == 0)
return true;
if(sa.charAt(0)!=sb.charAt(0))
return false;
return judgeStr(sa.substring(1),sb.substring(1));
}
练习7,辗转相除法/欧几里得算法求最大公约数
public static int gcd(int x,int y ){
if(y==0) return x;
return gcd(y,x%y);
}
练习8..在n个球中,任意取出m个不放回,有多少种取法
public static int ball(int n,int m){
if(n<m) return 0;
if(n==m) return 1;
if(m==0) return 1;
return ball(n-1,m-1)+ball(n-1,m);
//n个球中有一个特殊的球,那样每一次取球都可以看作取到这个特殊球或者不取到的两种情况的加和
}
注意有返回值类型的方法,它的出口条件返回数值是否可以满足题目要求,如返回0还是1
递归调用的原理
指令指针在递归时,指令指针所指内容和当前变量(现场)入栈,再将指令指针跳转至待递归的方法里
总结与提升练习
package algorithm.recrusion;
//区分直接递归与间接递归
//任何间接递归都可以等价地转化为直接递归调用
//如果递归调用的最后一条语句是递归调用语句,称这种递归调用是尾递归
/*
能够用递归解决的问题应该具有如下特征
1.需要解决的问题可以转化为一个或多个子问题求解,而子问题与原问题的解决方法相同,
只是数据规模不同
2.递归调用次数必须有限
3.必须有结束递归的条件
使用递归的常见情形
1.数学定义递归
2,数据结构递归
3.求解问题方法递归
*/
//递归模型是递归算法的抽象,一般,递归模型由递归出口和递归体组成,没有递归出口,会造成堆栈溢出
import java.util.Scanner;
//数据结构--结点
class Node{
int data;
Node next;
Node(int data){
this.data =data;
}
}
//数据结构 二叉树结点
class BiNode{
int data;
BiNode Lchild=null;
BiNode Rchild=null;
//叶子结点的构造方法
BiNode(int data)
{
this.data =data;
}
//非叶子结点的构造方法
BiNode(int data,BiNode lchild,BiNode rchild){
this.data =data;
Lchild =lchild;
Rchild = rchild;
}
}
public class Main {
//eg1,设计求n!的递归函数
public static int jiecheng(int n){
if(n==1)
return 1;
else
return n*jiecheng(n-1);//直接递归,尾递归
}
//eg2,求不带头结点的单链表的数据域和
public static int sumListData(Node node){
if(node==null)
return 0;
else
return node.data+sumListData(node.next);
}
//eg3,求二叉链存储的二叉树所有节点之和
public static int sumListBiTree(BiNode root){
if(root ==null)
return 0;
if(root.Lchild==null&&root.Rchild==null)
return root.data;
else
return sumListBiTree(root.Rchild)+sumListBiTree(root.Lchild)+root.data;
}
//eg4,hanoi问题
public static void move(int no,String from,String to){
System.out.println(no+"号盘子从"+from+"移动到"+to);
}
public static void hanoi(int n,String p1,String p2,String p3){
if(n==1){
move(1,p1,p3);
return;
}
hanoi(n-1,p1,p3,p2);//将n-1个碟子移动到p2
move(n,p1,p3);
hanoi(n-1,p2,p1,p3);
}
//eg4,fibonacci数列
public static int fib(int n){
if(n==1||n==2)
return 1;
else
return fib(n-1)+fib(n-2);
}
//求数组的最大元素
public static int maxArrayElem(int[] arr,int end){
if(end == 0)
return arr[0];
else
return Math.max(maxArrayElem(arr,end-1),arr[end]);
}
//简单选择排序.
//第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,
//然后再从剩余的未排序元素中寻找到最小(大)元素,继续放在起始位置
public static void selectSort(int a[],int n,int i){
int j, k;
if(i==n-1)
return;
else{
k=i;//用k记录最小元素下标
for(j =i+1;j<n;j++)
if(a[j]<a[k])//记录当前数据列最小元素下标为k
k=j;
if(k!=i)//最小元素不是i所指向的,swap k,i
{
int temp =a[i];
a[i]=a[k];
a[k]=temp;
}
selectSort(a,n,i+1);
}
}
//冒泡排序的思想
/*
1>比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2>每趟从第一对相邻元素开始,对每一对相邻元素作同样的工作,直到最后一对。
3>针对所有的元素重复以上的步骤,除了已排序过的元素(每趟排序后的最后一个元素),直到没有任何一对数字需要比较
*/
public static void BubbleSort(int a[],int n,int i){
int j;
boolean exchange;
if(i==n-1)
return;
else{
exchange=false;
for(j=n-1;j>i;j--)
if(a[j]<a[j-1]) //
{
int temp=a[j];
a[j]=a[j-1];
a[j-1]=temp;
exchange =true;
}
if(exchange=false)
return;
else
BubbleSort(a,n,i+1);
}
}
//n皇后
static int n;
static int column[] = new int[100];
static int count = 0;
static boolean check (int row,int col){
for(int i = 0 ; i < row ; i++)
if(column[i] == col || Math.abs(row-i) == Math.abs(column[i]-col))
return false;
return true;
}
static void dfs(int row){
if(row == n) {
count++;
return;
}
for(int c = 0 ; c < n; c++)
{
if(check(row,c)) {
column[row] = c;
dfs(row+1);
}
}
}
//测试用例
public static void main(String[] args) {
Node n1=new Node(1);
Node n2 =new Node(3);
Node n3 = new Node(5);
n1.next = n2;
n2.next =n3;
System.out.println("单链表结点和"+sumListData(n1));//以上为eg2测试用例
BiNode leaf1 = new BiNode(1);
BiNode leaf2 =new BiNode(3);
BiNode leaf3 = new BiNode(5);
BiNode bn1= new BiNode(7,leaf1,leaf2);
BiNode bn2 =new BiNode(9);
bn2.Rchild =leaf3;
BiNode root = new BiNode(11,bn1,bn2);
System.out.println("二叉树结点和"+sumListBiTree(root));
hanoi(3,"a","b","c");//hanoi测试用例
int num =fib(5);
System.out.println("fibonacci第五个的值:"+num);//fibonacci测试用例
//求数组最大元素用例
Scanner sc = new Scanner(System.in);
int len = sc.nextInt();
int []arr =new int[len];
for(int i = 0;i<len;i++)
{
arr[i]=(int)(100*Math.random());
System.out.print(arr[i]+" ");
}
int max = maxArrayElem(arr,len-1);
System.out.println("max:"+max);
//简单选择排序
for(int i = 0;i<len;i++)
{
arr[i]=(int)(100*Math.random());
System.out.print(arr[i]+" ");
}
selectSort(arr,len,0);
System.out.println("选择排序后");
for(int i:arr)
System.out.print(i+" ");
//冒泡排序
System.out.println();
for(int i = 0;i<len;i++)
{
arr[i]=(int)(100*Math.random());
System.out.print(arr[i]+" ");
}
BubbleSort(arr,len,0);
System.out.println("冒泡排序后");
for(int i:arr)
System.out.print(i+" ");
n=4;
dfs(0);
System.out.println("\n放置方法"+count);
}
}