递归思想的介绍和总结(Java实现)

递归的思想很简单(把规模大的、较难解决的问题——>规模较小的、易解决的同一类型问题。规模较小的问题又变成规模更小的问题,并且小到一定程度可以直接得出它的解,从而得到原来问题的解。)但是深入进去就很懵。

1.为了递归地求解问题,必须处理两种情况:

(1)基础情况,在这种情况下我们可以直接求解问题。

例如汉诺塔问题中,当盘子个数为1时,我们直接将盘子从第一根柱子移到第三根(第二根看作备用柱子);也就是说值为1为一种基础情况。

(2)递归情况,在这种情况下我们必须依据更容易的子问题来求解问题。(更容易指它们必须更接近基础情况。例如汉诺塔问题中,依据移动 n-1 张圆盘这个更容易的问题求解移动n张盘子的问题。)

用下面的话再来解释一下:
1)用递归去解决问题时,例如F(n)的问题,转化为F(n-1)的问题,且这两者的处理模式一样——>使对象之间有规律的递增或递

;(递归情况)

2)必定要有一个明确的结束递归的条件(基础情况),否则递归将会无止境地进行下去,直到耗尽系统资源。

原文链接:https://blog.csdn.net/ggxxkkll/article/details/7524056+https://www.cnblogs.com/Shinea_SYR/p/9655181.html

2.递归的调度机制

(1)当程序执行到一个方法时,就会开辟一个独立的空间(栈);

(2)每个空间的数据(局部变量)都是独立的;

3.使用递归来设计归并排序

其递归思想:(1)如果只有一个数字要排序,则不做任何事;(2)否则,将数字分成两组,递归排序每个组,使有序后归并到一个有序数组中。

归并排序使分治算法的一个实例。(分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序算法,简单问题可用二分法完成。)

import java.util.Arrays;

public class MergeSort {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] arr = new int[] { 1, 2, 5, 2, 4, 6, 8, 10 };
		arr = mergerHelper(arr, 0, arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}

	// merge改进 1,2,5,2,4,6,8,10
	public static int[] mergerHelper(int[] data, int bottom, int top) {
		if (bottom == top) {
			return new int[] { data[bottom] };// 重合,只有单个元素的数组,直接返回
		} else {
			int midpoint = (top + bottom) / 2;
			return merge(mergerHelper(data, bottom, midpoint), mergerHelper(data, midpoint + 1, top));
		}
	}

	public static int[] merge(int[] a, int[] b) {
		int[] result = new int[a.length + b.length];
		int i = 0;// 跟踪a数组的下标
		int j = 0;// 跟踪b数组的下标
		for (int k = 0; k < result.length; k++) {
			if (j == b.length || (i < a.length && a[i] <= b[j])) {
				result[k] = a[i++];
			} else {
				result[k] = b[j++];
			}
		}
		return result;
	}

}

在对较大的数组排序时,归并排序更快一些。

3.快速排序的递归实现

快速排序是另一种分治排序算法。下面是快速排序的计划

(1)如果有一个或较少的数字要排序,则不做任何事情。

(2)否则,把区域分成较小数字和较大数字两部分,把较小的数字移到左边,较大的移到右边。递归的对每个区域进行排序。

算法实现:首先随意选一个元素(第一个或最后一个较好)作为基准(pivot),</=pivot的数字看成较小数字,>pivot的数字看作较大的数字。该算法维持4个区域:小的数、大的数、未检查的数、包含基准的区域。最后一个动作是把基准pivot交换进小的数和大的数之间的位置。时间复杂度:O(n log n)

代码在我快速排序那一博客里面。

4.避免递归:每当我们调用一个方法时,就不得不把一个帧压到调用栈上。这花费时间和内存。

方法:尾部递归

LinkedList类的get()方法

//原来的递归方法
public Object get(int index) {
    return getHelper(index, front);
}
public E getHelper(int index, ListNode <E> node){
    if(index==0) {
        return node.getItem();
    } else {
      return getHelper(index-1, node.getNext());
    }
}

将其转换为一种迭代算法

public E getHelper(int index, ListNode <E> node) {
  while(true) {
    if(index==0) {
        return node.getItem();
    } else {
        index--;
        node = node.getNext();
    }
}

我没有怎么看懂,但是在这种情况下后面的效率会高一点。

5.动态规划

例如Fibonacci数,为了避免一些重复计算,我们用动态规划(用数组来存放以前计算过的值,当我们为n的每个值计算F(n)时,我们可以查找任何更小的n值)。

这个方法的运行时间时n 的线性函数。

public static int fibo(int n) {
    int[] f = new int[n + 1];
    f[0] =1;
    f[1] =1;
    for(int i =2; i <=n; i++) {
        f[i] = f[i - 1] + f[i - 2];
    }
    return f[n];
}

用动态规划把以前计算的值存储在数组中,以避免冗余。

递归允许设计强大、优雅的算法,但它会为调用栈用光时间和空间。虽然这种情况不总是可能发生,但是有时可以通过消除递归来提高效率。可以把尾部递归算法(在该算法中,递归调用是最后一步)轻松地转换成一个循环。如果算法只是返回一个值(与修改现有的数据结构相对),就可以通过把以前调用的结果存储在一张表中来避免冗余的计算。这一种技术称为动态规划。


相关术语:
分治法(divide and conquer): 一种算法,其工作方式是把数据分成几块,递归地来能各个块,然后重新合并解。归并排序和快速排序都是分治算法。
动态规划(dynamic progranming): 用于提高做冗余工作的递归算法效率的技术。它会把子问题的解存储起来,使得可以查找而不是重新计算它们。
辅助方法(helper method):一种方法,通常是受保护的方法,用在递归算法中。递归的辅助方法通常需要一些额外的参数, 来指定正在处理的子问题。
原位(in place):一 种排序方法,用于在原数组内部把元素移来移去,而不用创建新的数据结构。
迭代(iterative): 一种算法,使用循环而不是递归。
递归方程(recurrence): 依据函数自身来定义一个函数的方程。用于分析递归算法的运行时间。
递归树(recursion tree):用于求解递归方程的技术,其工作方法是:反复用其递归定义的成分代替函数。
尾部递归( tail recursive):一种递归算法, 让递归调用成为返回前的最后一步。

end.

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值