递归 Recursion
递归,以相似的方式重复自身的过程;
在程序中的表现是:
在函数的定义中直接或间接调用函数自身
递归是一个 树结构,从字面可以其理解为重复“递推”和“回归”的过程,当“递推”到达底部时就会开始“回归”,其过程相当于树的深度优先遍历
递归的应用
条件
- 原问题必须可以分解为若干子问题,而且子问题必须与原问题为相同或者相似的问题,且规模更小
- 子问题的演化必须有一个明确的终点,否则可能导致无限递归(即无终止条件的循环);也即是,必须有一个出口,化简为非递归情况处理
形式
def recursion(大规模问题) :
if end_condition: # 终止条件
end # 终止的处理
else:
recursion(小规模问题) # 调用自身
场景
- 问题的定义是按递归定义的(阶乘和Fibonacci函数等)
- 问题的解法是递归的(汉诺塔等一些问题只能使用递归的方式)
- 数据结构是递归的(链表和树等的一些操作)
更详细的讲解,可以查看如下链接:数据结构与算法之递归
递归与循环
-
递归是 有去(递去)有回(归来),因为存在终止条件,比如你打开一扇门还有一扇门,不断打开,最终你会碰到一面墙,然后返回
-
循环是 有去无回,但可以设置终止条件,比如你打开一扇门还有一扇门,不断打开,还有门,没有终点
递归的优缺点
优点:
简洁,容易处理问题,代码可读性高
缺点:
时间和空间的消耗大
迭代
迭代,为了逼近所需要的目标和结果,不断使用变量的旧值递推新值的过程
迭代在程序中的表现:
函数不断调用原函数的返回值
迭代是一个 环结构,从初始状态开始,每次迭代都遍历这个环,并更新状态,多次迭代直到到达结束状态
迭代的应用
条件
- 必须有返回值可以作为再次迭代的初值
形式
def iteration(A):
for i in range(n):
B = iteration(B)
return B
场景
- 递归中一定有迭代,但是迭代中不一定有递归,大部分可以相互转换,一般能用迭代的不用递归
迭代、循环与递归
迭代与循环
(1)循环:参与运算的变量同时是保存结果的变量
(2)迭代:当前保存的结果作为下一次循环计算的初始值。迭代则使用计数器结束循环
迭代与递归
(1)迭代:函数内某段代码实现循环,函数调用时使用前一次循环的返回值作为初始值,A调用B,使用计数器结束循环
(2)递归:重复调用自身实现循环,A调用A,设置结束条件
迭代的优缺点
优点:
代码效率高,时间空间消耗比递归小
缺点:
不够简洁,容易混淆
代码比较 (Java
)
计算阶乘
递归:
public int factorial(int n) {
if(n == 1) {
return 1;
} else {
return n*factorial(n - 1);
}
}
迭代:
public int factorial(int n) {
int result = 1;
for(int i = 0; i < n; i++) {
result *= 1;
}
return result;
}
对称二叉树
题目描述
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3]
是对称的:
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3]
则不是镜像对称的:
1
/ \
2 2
\ \
3 3
题目分析
如果一个树的左子树与右子树镜像对称,那么这个树是对称的
如果同时满足下面的条件,两个树互为镜像:
- 它们的两个根结点具有相同的值
- 每个树的右子树都与另一个树的左子树镜像对称
递归:
可以实现这样一个递归函数,通过 同步移动 两个指针的方法来遍历这棵树,p
指针和 q
指针一开始都指向这棵树的根,随后 p
右移时,q
左移,p
左移时,q
右移
每次检查当前 p
和 q
节点的值是否相等,如果相等再判断左右子树是否对称
迭代:
那么如何用迭代的方法实现呢?
首先引入一个队列,这是把递归程序改写成迭代程序的常用方法。
初始化时,把根节点入队两次。每次提取两个结点并比较它们的值(队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像),然后将两个结点的左右子结点按相反的顺序插入队列中
当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续结点)时,该算法结束
代码示例
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
// 递归
class Solution {
public boolean isSymmetric(TreeNode root) {
boolean result;
TreeNode left = root.left;
TreeNode right = root.right;
result = judge(left, right);
// 或者直接如下
// result = judge(root, root);
return result;
}
public boolean judge(TreeNode left, TreeNode right) {
if (left == null && right == null) {
return true;
}
if (left == null || right == null) {
return false;
}
return left.val == right.val && judge(left.right, right.left) && judge(left.left, right.right);
}
}
// 迭代
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root, root);
}
public boolean check(TreeNode u, TreeNode v) {
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.offer(u);
q.offer(v);
while (!q.isEmpty()) {
u = q.poll();
v = q.poll();
if (u == null && v == null) {
continue;
}
if ((u == null || v == null) || (u.val != v.val)) {
return false;
}
q.offer(u.left);
q.offer(v.right);
q.offer(u.right);
q.offer(v.left);
}
return true;
}
}