一定要明白递归函数中每个参数的含义,这样在逻辑处理和函数调用的时候才能得心应手,
函数的调用我们一定不要去一步步拆开去
递归模板
递归必须具备两个条件,一个是调用自己,一个是有终止条件。这两个条件必须同时具备,且一个都不能少。并且终止条件必须是在递归最开始的地方,也就是下面这样
递归就是在函数内部调用自身的函数。
public void recursion(参数0) {
if (终止条件) {
return;
}
recursion(参数1);
}
不能把终止条件写在递归结束的位置
实例分析
递归的理解是先往下一层层传递,当碰到终止条件的时候会反弹,最终会反弹到调用处。下面我们就以5个最常见的示例来分析下
最简单的递归调用-阶乘
public int recursion(int n) {
if (n == 1) {
return 1;
}
return n * recursion(n - 1);
}
递归的目的是把一个大的问题细分为更小的子问题,我们只需要知道递归函数的功能即可,不要把递归一层一层的拆开来想,
斐波那契数列
道斐波那契数列当前的值是前两个值的和
public int recursion(int n) {
if (n == 1 || n == 2) {
return 1;
}
return recursion(n-1)+recursion(n-2);
}
前序遍历
public void recursion(TreeNode treeNode) {
// 终止条件 (必须要有)
if (treeNode == null) {
return;
}
// 逻辑处理 (不是必须的)
System.out.println("treeNode.val = " + treeNode.val);
// 递归调用 (必须要有)
recursion(treeNode.left);
recursion(treeNode.right);
}
中序遍历
public void recursion(TreeNode treeNode) {
// 终止条件 (必须要有)
if (treeNode == null) {
return;
}
// 逻辑处理 (不是必须的)
// 递归调用 (必须要有)
recursion(treeNode.left);
System.out.println("treeNode.val = " + treeNode.val);
recursion(treeNode.right);
}
后序遍历
public void recursion(TreeNode treeNode) {
// 终止条件 (必须要有)
if (treeNode == null) {
return;
}
// 逻辑处理 (不是必须的)
// 递归调用 (必须要有)
recursion(treeNode.left);
recursion(treeNode.right);
System.out.println("treeNode.val = " + treeNode.val);
}
在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。
递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。(效率)
递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。(效率)
调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。(性能)
package com.softsec;
/**
* 递归计算n的阶乘
* @author Chenhh
*/
public class demo {
public static void main(String[] args) {
System.out.println(recursion(5));
}
/**
* 递归计算n的阶乘
*/
private static int recursion(int n) {
if (n <1) {
throw new IllegalArgumentException("参数必须大于0");
} else if (n == 1) {
return 1;
} else {
return n * recursion(n - 1);
}
}
}
class Node {
private int Data;// 数据域
private Node Next;// 指针域
}
/**
public class javatest1 {
public static void main(String[] args) {
Node head = new Node(0);
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
head.setNext(node1);
node1.setNext(node2);
node2.setNext(node3);
Node h = head;
while (null != h) {
System.out.print(h.getData() + " ");
h = h.getNext();
}
head = Reverse1(head);
while (null != head) {
System.out.print(head.getData() + " ");
head = head.getNext();
}
}
/**
* 递归,在反转当前节点之前先反转后续节点
*/
public static Node Reverse1(Node head) {
// head看作是前一结点,head.getNext()是当前结点,reHead是反转后新链表的头结点
if (head == null || head.getNext() == null) {
return head;// 若为空链或者当前结点的下一个节点在尾结点,则直接还回
}
Node reHead = Reverse1(head.getNext());// 先反转后续节点head.getNext()
head.getNext().setNext(head);// 将当前结点的指针域指向前一结点
head.setNext(null);// 前一结点的指针域令为null;
return reHead;// 反转后新链表的头结点
}
}