递归算法

3 篇文章 0 订阅

递归算法

​ 在函数或子过程的内部,直接或者间接地调用自己的算法。


特点:

  • (1) 递归就是在过程或函数里调用自身。
  • (2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
  • (3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
  • (4) 在 递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成 栈溢出等。所以一般不提倡用递归算法设计程序。

递归算法一般用于解决三类问题:

递归设计经验:

  • 找重复(子问题)
  • 找重复中的变化量–>参数
  • 找参数变化趋势 --> 设计出口

解递归的常见思路:

  1. 切蛋糕思维,如求阶乘f(n) = n * f(n-1),数组就和,翻转字符串。
  2. 化不开的,看有没有递推公式?有没有等价转化? 如:斐波那契数列f(n) = f(n-1) + f(n-2),求最大公约数:f(m,n) = f(n, m%n)。

递归算法C语言实例

利用递归实现1到100以内的求和;

#include<stdio.h>
int main()
{
  int Sum(int n);
  printf("sum=%d\n",Sum(100));
  return 0;
}

int Sum(int n)
{
  int sum=0;
  if(n==0)
    return 0;
  else
    return sum=n+Sum(n-1);
}

利用递归求阶乘:

#include<stdio.h>
int main()
{
    int Fac(int n);
    printf("f=%d\n",Fac(5));
    return 0;
}
int Fac(int n)
{
    int f=0;
    if(n==1)
        return f=1;
    else
        return f=n*Fac(n-1);
}

利用递归求数组中最大数:

#include<stdio.h>
int main()
{
    int Max(int a[],int n);
    int a[5]={4,75,23,300,53};
    printf("最大数是%d\n",Max(a,5));
    return 0;
}
int Max(int a[],int n)
{
    if(n==1)
        return a[0];
    else
    {
         if(a[n-1]>Max(a,n-1))
            return a[n-1];
         else
            return Max(a,n-1);
    }
}

递归算法java语言实例:

斐波那契序列:

斐波那契数列问题,等价于两个子问题:

  1. 求前一项
  2. 求前两项
  3. 两项求和
public class 斐波那契序列 {

	public static void main(String[] args) {
		for(int i = 1; i < 10; i++) {
			System.out.print(fibonacci(i) + " ");
		}
	}
	
	public static int fibonacci(int n) {
		if(n == 1 || n == 2)
			return 1;
		return fibonacci(n-1) + fibonacci(n-2);
	}
}

运行结果:

1 1 2 3 5 8 13 21 34 

最大公约数:

public class 最大公约数 {
	public static void main(String[] args) {
		System.out.println(gcd(24,30));
	}
	public static int gcd(int m, int n) {
		if(n == 0) return m;
		return gcd(n, m%n);
	}
}

程序运行结果:

6

插入排序改递归:

public class 递归_插入排序 {
	public static void main(String[] args) {
		int[] arr = {9, 18, 2, 3, 6 ,4, 0};
		insertSort(arr, arr.length-1);
		for(int i = 0; i < arr.length; i++)
			System.out.print(arr[i] + " ");
	}
	
	// 对数组0-倒数第一个排序
	// 等价于:
	// 	对数组0-倒数第二个元素排序,
	//	然后把最后一个元素插入到这个有序的部分中
	public static void insertSort(int[] arr, int k) {
		if(k == 0)
			return;
		//对前k-1个元素排序
		insertSort(arr, k-1);
		//把位置k的元素插入到前面的部分
		int x = arr[k];
		int index = k-1;
		while(index >= 0 && x < arr[index]) {
			arr[index+1] = arr[index];
			index--;
		}
		arr[index+1] = x;
	}
}

程序运行结果:

0 2 3 4 6 9 18 

汉诺塔问题:

将1-N从A移动到B,C作为辅助。等价于:

  1. 1~N-1移动到C,A作为源,B作为辅助
  2. 把N从A移动到B
  3. 把1~N-1 从C移动到B,A为辅助
public class 汉诺塔 {
	public static void main(String[] args) {
		printHanoiTower(3, "A", "B", "C");
	}
	/**
	 * 将1-N从A移动到B,C作为辅助。等价于:
	 *		1. 1~N-1移动到C,A作为源,B作为辅助
	 *		2. 把N从A移动到B
	 *		3. 把1~N-1 从C移动到B,A为辅助
	 * @param N N个盘子
	 * @param from 放盘子的初始柱子
	 * @param to 目标柱子
	 * @param help 辅助柱子
	 */
	public static void printHanoiTower(int N, String from, String to, String help) {
		if(N == 1) {
			System.out.println(N + ": " + from + "-->" + to);
			return;
		}
		// 先把前N-1个盘子挪到辅助空间上去
		printHanoiTower(N-1, from, help, to);
		// N可以顺利到达目标
		System.out.println(N + ": " + from + "-->" + to);
		// 让N-1个盘子从辅助空间回到"原位置上"
		printHanoiTower(N-1, help, to, from);
	}
}

程序运行结果:

1: A-->B
2: A-->C
1: B-->C
3: A-->B
1: C-->A

二分查找递归解法:

全范围二分查找,等价于三个子问题:

  1. 左边找(递归)
  2. 中间比
  3. 右边找(递归)

注意:左边查找和右边查找只选其一

public class 递归_二分查找 {
	public static void main(String[] args) {
		int[] arr = {1, 3, 9, 13, 19, 23, 25};
		System.out.println(binarySearch(arr, 0, arr.length-1, 13));
	}
	
	public static int binarySearch(int[] arr, int low, int high, int key){
		if(low > high)
			return -1;
		int mid = low + ((high-low)>>1);//(low+high)>>1
		int midVal = arr[mid];
		if(midVal < key) //向左找
			return binarySearch(arr, mid + 1, high, key);
		else if(midVal > key) //向右找
			return binarySearch(arr, low, mid - 1, key);
		else 
			return mid; //key found
	}
}

程序运行结果:

3

递归算法的性能分析:

在这里插入图片描述

斐波那契数列递归实现改进:

通过记录每次f(n)的值来防止大量重复计算,大大增加了性能。

public static int fib(int n, int[] tmp) {
		
		if(tmp[n] != 0) {
			return tmp[n];
		}
		if(n == 1 || n == 2)
			return 1;
		tmp[n] = fib(n-1, tmp) + fib(n-2, tmp);
		return fib(n-1, tmp) + fib(n-2, tmp);
	}

改进前和改进后性能对比:

public class 斐波那契序列 {
	static int count = 0;
	static int count1 = 0;
	public static void main(String[] args) {
		for(int i = 1; i < 20; i++) {
			System.out.print(fibonacci(i) + " ");
		}
		System.out.println();
		System.out.println("运算次数:" + count);
		int[] tmp = new int[20];
		for(int i = 1; i < 20; i++) {
			System.out.print(fib(i, tmp) + " ");
		}
		System.out.println();
		System.out.println("运算次数:" + count1);
	}
	
	public static int fibonacci(int n) {
		if(n == 1 || n == 2)
			return 1;
		count++;
		//System.out.print("<" + fibonacci(n-1) + fibonacci(n-2)+ ">") ;
		return fibonacci(n-1) + fibonacci(n-2);
	}
	
	public static int fib(int n, int[] tmp) {
		
		if(tmp[n] != 0) {
			return tmp[n];
		}
		if(n == 1 || n == 2)
			return 1;
		tmp[n] = fib(n-1, tmp) + fib(n-2, tmp);
		//System.out.print("<" + tmp[n] + ">");
		count1++;
		return fib(n-1, tmp) + fib(n-2, tmp);
	}
}

程序运行结果:

1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 
运算次数:10926
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 
运算次数:17

小白上楼梯:

小白正在上楼梯,楼梯有n阶台阶,小白一次可以上1阶,2阶或者3阶,实现一个方法,计算小白有多少种走完楼梯的方式。

package chapter2;

public class 小白上楼梯 {

	public static void main(String[] args) {
		
		long now = System.currentTimeMillis();
		for(int i = 1; i < 30; i++) {
			int[] tmp = new int[1000];
			System.out.print(f(i, tmp)+ " ");
		}
		System.out.println();
		long end = System.currentTimeMillis();
		System.out.println(end-now + "ms");
		
		long now1 = System.currentTimeMillis();
		for(int i = 1; i < 30; i++) {
			System.out.print(f1(i) + " ");
		}
		System.out.println();
		long end1= System.currentTimeMillis();
		System.out.println(end1-now1 + "ms");
		
			
	}
	
	public static int f(int n, int[] tmp) {
		if(tmp[n] != 0) return tmp[n];
		if(n == 1) return 1;
		if(n == 2) return 2;
		if(n == 3) return 4;
		tmp[n] = f(n-1, tmp) + f(n-2, tmp) + f(n-3, tmp);
		return f(n-1, tmp) + f(n-2, tmp) + f(n-3, tmp);
	}
	public static int f1(int n) {
		if(n == 1) return 1;
		if(n == 2) return 2;
		if(n == 3) return 4;
		return f1(n-1) + f1(n-2) + f1(n-3);
	}
}

程序运行结果:

1 2 4 7 13 24 44 81 149 274 504 927 1705 3136 5768 10609 19513 35890 66012 121415 223317 410744 755476 1389537 2555757 4700770 8646064 15902591 29249425 
1ms
1 2 4 7 13 24 44 81 149 274 504 927 1705 3136 5768 10609 19513 35890 66012 121415 223317 410744 755476 1389537 2555757 4700770 8646064 15902591 29249425 
98ms
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值