递归与迭代分析 —— 以 Leetcode 例题(对称二叉树)为例

递归 Recursion

递归,以相似的方式重复自身的过程;

在程序中的表现是:
在函数的定义中直接或间接调用函数自身

递归是一个 树结构,从字面可以其理解为重复“递推”和“回归”的过程,当“递推”到达底部时就会开始“回归”,其过程相当于树的深度优先遍历

digui

递归的应用

条件
  • 原问题必须可以分解为若干子问题,而且子问题必须与原问题为相同或者相似的问题,且规模更小
  • 子问题的演化必须有一个明确的终点,否则可能导致无限递归(即无终止条件的循环);也即是,必须有一个出口,化简为非递归情况处理
形式
def recursion(大规模问题) :
	if end_condition:   # 终止条件
		end             # 终止的处理
	else:
		recursion(小规模问题)  # 调用自身
场景
  • 问题的定义是按递归定义的(阶乘和Fibonacci函数等)
  • 问题的解法是递归的(汉诺塔等一些问题只能使用递归的方式)
  • 数据结构是递归的(链表和树等的一些操作)

更详细的讲解,可以查看如下链接:数据结构与算法之递归

递归与循环

  1. 递归是 有去(递去)有回(归来),因为存在终止条件,比如你打开一扇门还有一扇门,不断打开,最终你会碰到一面墙,然后返回

  2. 循环是 有去无回,但可以设置终止条件,比如你打开一扇门还有一扇门,不断打开,还有门,没有终点

递归的优缺点

优点:

简洁,容易处理问题,代码可读性高

缺点:

时间和空间的消耗大

迭代

迭代,为了逼近所需要的目标和结果,不断使用变量的旧值递推新值的过程

迭代在程序中的表现:
函数不断调用原函数的返回值

迭代是一个 环结构,从初始状态开始,每次迭代都遍历这个环,并更新状态,多次迭代直到到达结束状态

diedai

迭代的应用

条件
  • 必须有返回值可以作为再次迭代的初值
形式
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 右移

每次检查当前 pq 节点的值是否相等,如果相等再判断左右子树是否对称

迭代:

那么如何用迭代的方法实现呢?
首先引入一个队列,这是把递归程序改写成迭代程序的常用方法。

初始化时,把根节点入队两次。每次提取两个结点并比较它们的值(队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像),然后将两个结点的左右子结点按相反的顺序插入队列中

当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续结点)时,该算法结束

代码示例
/**
 * 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;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值