递归

递归

树的面试题解法一般都是递归
1 节点的定义(树的节点和树本身数据结构的定义就是用递归方式进行的)
2 重复性(不仅树本身,二叉树以及搜索二叉树,比如二叉搜索树,它的左子树都要小于根节点,右子树都要大于根节点,且左右子树具有相同的特征)

def preorder(self, root):
if root:
	self.traverse_path.append(root.val)
	self.preorder(root.left)
	self.preorder(root.right)
def inorder(self, root):
if root:
	self.inorder(root.left)
	self.traverse_path.append(root.val)
	self.preorder(root.right)
def postorder(self, root):
if root:
	self.postorder(root.left)
	self.postorder(root.right)
	self.traverse_path.append(root.val)
	
递归 Recursion: 递归本身就是循环,通过函数体来调用自己来进行循环

因为计算机本身用的汇编,而汇编有个特点,就是没有所谓的循环嵌套这么一说,很多时候用的
更多的是你之前有一段函数写在什么地方,或者一段指令写在什么地方,就会直接不断地反复跳到
之前的那段指令,不断的执行,其实这就是所谓的递归
循环本身编译处理后的汇编代码,其实和递归本身有异曲同工之处, 所以递归和循环没有明显的边界

现实中的递归,现实生活中也有所谓的重复性,想到来自一部电影"盗梦空间" 就有归去来兮的感觉
主线:从飞机开始,然后到城市,再往下不断地递归, 最后在一个雪上的屋子里
每进入一层递归就是 一个新的世界,或者互不干扰的世界,在里面做些事情
再下到另外一层再做些事情, 如果要返回现实世界,必须一层一层再回来
每次下去和回来的时候自身发生改变(把自己的状态带到下一层,发生改变后再带回来)(参数)
但是环境里面的东西是不受影响, 下一层的环境不会影响这一层的环境和人物,那么主角
和主角相关的人是函数的参数

盗梦空间 递归要点:
1 向下进入到不同梦境中:向上回到原来一层 (一层一层下, 一层一层回来,不能跳跃 对称性,归去来兮的感觉)
2 通过声音同步回到上一层(所谓同步就是用参数来进行函数不同层之间传递变量)
3 每一层环境和周围的人都是一份拷贝,也就是进入每一层的房子建筑,电脑,无关的人物,其实就是
创造了一份新 的世界,当吧这个世界全部打坏了,去到下一层和上一层它们的建筑都是不受影响的
主角可以穿越不同的梦境同时把自身和自己所有携带的东西,都可以带到不同梦境发送变化且可以把变化携带回来(类似于函数参数),也会有一些全局变量

计算n!

# n! = 1 * 2 * 3…n
def Factorial(n):
	if n <=1
		return 1;
	return n * Factorial(n - 1)

在这里插入图片描述

递归运行方式递归栈,系统就给我们做了这样一个调用栈(一层一层展开,更像剥洋葱.类似于栈的形式,一层一层进去,再剥开),而栈本身就是递归调用的时候系统做了这样一个调用栈
递归代码模板

def recursion(level, param1, param2,):
# recursion terminator
	if level > MAX_LEVEL:
	process_result
	return 
# process logic in current level
	process(level, data…)
#drill down
	self.recursion(level + 1, p1,)
# reverse the current level status if needed (链表 p.next.next = p)

public void recur(int level, int param) {
	// terminator
	if (level > MAX_LEVEL) {
		// process result
		return;
	}
	// process current logic
	process(level, param);
	
	//  drill down
	recur(level+1, newPara…)

	// restore current status	
}

最后一部分,在这一层有些东西可能要清理,就清理下,有些时候可能不需要这一部,因为这一层很多时候,本身的环境是拷贝一份出来的,但也有很多时候会有一些全局变量要进行清理,就在最后这部分进行清理即可

public ListNode reverseList(ListNode head) {
	// teminator
	if (head = null || head.next == null) {
		return head;
	}
	// process logic in current level and drill down
	ListNode pre = reverseList(head.next);
	
	// reverse the current level status if needed
	head.next.next = head;
	head.next = null;
	return pre;
}

写递归,
第一部分,递归终止条件, 一定要先把递归终止条件写上(这点不注意就会造成结果是无线递归或者死循环)
第二部分, 处理当前层逻辑
第三部分, 下到下一层去 (用参数标记当前层是哪一层即将level变成 level + 1同时传递相应的参数)
第四部分: 清扫当前层(有些时候不需要清理,因为这一层本身环境是拷贝处理的,但很多时候会有一些全局变量进行清理)

递归思维要点:
1 抵制人肉进行递归(最大误区) 应该直接看函数本身写 // 主要是针对熟练的人 初学者要花递归状态树
2 找最近重复性(找到最近最简方法,将其拆解成可重复解决的问题)
3 数学归纳法思维 (最开始条件成立,比如n = 1,n =2 的时候成立,且可以证明当n成立的时候可以推导出n+1也成立)

递归实战

爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶
    示例 2:
    输入: 3
    输出: 3
    解释: 有三种方法可以爬到楼顶。
  3. 1 阶 + 1 阶 + 1 阶
  4. 1 阶 + 2 阶
  5. 2 阶 + 1 阶
    傻递归
public int climStairs(int n) {
	clim_Stair(0, n);
}
public int clim_Stair(int i, int n) {
	// terminator
	if (i > n) {return 0;}
	if (i == n) {return 1;}
	// process current level and drill down
	return clim_Stair(i + 1, n) + clim_Stair(i + 2, n);
}

记忆化

public int climStairs(int n) {
	int mem[] = new int[n + 1];
	clim_Stair(0, n, mem);
}
public int clim_Stair(int i, int n, int[] mem) {
	// terminator
	if (i > n) {return 0;}
	if (i == n) {return 1;}
	if (mem[i] > 0) {
		return mem[i];
	}
	// process current level and drill down
    mem[i] = clim_Stair(i + 1, n, mem) + clim_Stair(i + 2, n, mem);
	return mem[i]
}

动态规划

public int climStairs(int n) {
	if (n == 1) {return 1;}
	int dp[] = new int[n + 1];
	dp[1] = 1;
	dp[2] = 2;
	for (int i = 3; i <= n; i++) {
		dp[i] = dp[i - 1] + dp[i - 2];
	}
	return dp[n];
}
public int climStairs(int n) {
	if (n == 1) {return 1;}
	int first = 1;
	int second = 2;
	for (int i = 3; i <= n; i++) {
		second = first + second;
		first = second - first;
	}
	return second;
}
#1: 1
#2: 2
#3: f(1) + f(2) mutual exclusive 互斥, complete exhausive 所有可能都包含
#4: f(2) + f(3)
#f(n) : f(n - 1) + f(n - 2) fibonacci
def climbStairs(self, n):
	if (n <= 2): return n;
	f1, f2, f3 = 1, 2, 3
	for i in rang(3, n):
		f3 = f2 + f1
		f1 = f2
		f2 = f3
   return f3

括号生成

public List<String> generateParenthese(int n) {
// 总共2n个格子
	_generate(0, 0, n, “”);
}
List<String> result = new ArrayList<String>();
private void _generate(int left, int right, int n, String s) {
	 // terminator
	if (left == n && right == n) {
		System.out.println(s);
		result.add(s);
		return;
	}
	// process current logic: left

	if left < n {
		// drill down
		_generate(left + 1, right, n,  s +();
	}
	
	// process current logic: right
	if (left > right && right < n) {
		// drill down
		 _generate(left, right + 1, n,  s +));
	}
	// reverse states  不需要因为都是局部变量, 本地变量,自己清除
}

// left:随时可以加,只要别超标
// right: 必须之前有左括号,且左个数 > 右个数

验证二叉搜索树, 最近公共祖先, 二叉树的深度
BST(binary search tree) :中序遍历是递增的
理解起来一遍看不明白很正常(要反复看几次)这个过程要掌握
解题与学习过程
错误思路:不看左子树也不看右子树,只看了左节点和右节点,这个是错误的,不仅仅要看它的左儿子和右儿子和根节点的大小,还要看整个左子树右子树的大小
二叉树最大深度是什么(重复项)
最大深度来自两个地方
1 左子树深度 + 根
2 右子树深度 + 跟

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值