Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

学习笔记

递归

模板

  1. 首先规定递归什么时候结束,一定要限制条件,不能无限循环

  2. 递归的执行逻辑,当前层的执行逻辑,即重复性思维,重复性处理,找相似性

  3. 递归调用,层数加1,进入下一层

  4. 最后清除,如果有涉及到外部逻辑的

func recursion(level int, param1, param2 interface{}) {
	// recursion terminator递归跳出条件
	if level > MAX_LEVEL {
		process_result
 		return
	}
 		
	// process logic in current level 当前层的执行逻辑
	process(level, data...)

	// drill down递归调用
	recursion(level + 1, p1, ...)

	// reverse the current level status if neede 清除收尾
}
	

注意点

1.递归要找到重复子问题,不要人肉递归

2.一定注意递归终止条件

3.递归属于后进先出(与栈,深度优先遍历类似)

分治

1.分治是将递归过程中的问题分步骤解决,每一步骤单独递归,最终得到结果

func recursion(level int, param1, param2 interface{}) {
	// recursion terminator递归跳出条件
	if level > MAX_LEVEL {
		process_result
 		return
	}
 		
	// 分治处理当前层逻辑
	for {
		// 
		process(...)
		// drill down递归调用
		recursion(...)
	}

	// reverse the current level status if neede 清除收尾
}
	

回溯

思想

1.回溯采用试错的思想,分布解决,达到某种特定条件,或者是错误结果,则进行回溯,要有一个不断变化的变量,递归下一层改变,回溯上一层重置

2.递归过程中是一直向深层递归,处理完某种特定条件之后,返回当前层,回到上一层

3.对于当前层的处理进行回溯,回到上一层的状态,进行状态重置

4.在上一层做相应处理,通过分治,走下一种情况去解决,进入到另一种情况下一层,继续处理

5.重复4步骤,知道递归完毕

例如: 组合,全排列等题

1.通过递归往数组里添加数字,直到这个数组,满足结果,添加进最终结果,这个数组就是不断变化的变量

2.然后回溯,将状态回到上一层,再对上一层的分治的第二种情况,继续向下递归,然后回溯,重复步骤

3.区别在于分治时候的逻辑不一样,组合是依次遍历+1,所以不会加入重复的, 全排列是全遍历,要对于已加入的数字进行过滤

复杂度分析

由于回溯相当于遍历,所以时间复杂度会比较高,通过特定条件进行剪枝,跳过一些情况,可以降低复杂度

比如全排列2的排序操作,通过排序预处理,针对已经加入,或者重复的进行跳过,达到剪枝,减少递归

时间复杂度: O(N×N!) 空间复杂度: O(N×N!)

理解

1.为什么要进行回溯,不回溯的话也可以解决问题,则需要在每一层递归的时候,创建新的变量来处理问题,消耗比较大

2.回溯则是用一个变化的变量,进行修改状态,重置状态,不用创建变量

3.回溯问题思考: 1.如何产生分支,如何进行下次递归,递归树是怎样的 2.解在哪里,是在叶子节点,还是路径,还是非叶子节点,如何得到解 3.哪些情况不需要解,如何过滤掉这些解,如何剪枝,如何避免重复

func recursion(level int, param1, param2 interface{}) {
	// 得到解,解的条件
	terminator(...) {
		return
	}
	// 分治处理
 	for {
 		// 剪枝判断,剪枝过滤,减少递归
 		fix(...)
 		// 得到解的过程,当前层处理逻辑,变化变量的处理
		process(...)

		// 进入下一层,变化的变量传入下一层
		recursion(...)

		// 回溯,回溯变化的变量,回到上一层之后的处理
		back()
 	}
}