常见递归题目:
斐波那契数列
修剪二叉搜索树
重建二叉树
求二叉树的坡度
最长同值路径
二叉树的堂兄弟节点
学习递归经常告诉我们,写出递归公式就完事了。但是真实情况往往并不那么简单。
递归是一种算法结构,回溯是一种算法思想
DFS深度优先搜索是基于递归算法结构。(DFS也可以使用栈来实现)
一般就是基于递归结构来计算是否存在,搜索到和搜索不到,主要是利用栈,不保留旧的经历的路径。
回溯复杂一些,回溯的话我们一般显式的包含有撤回到原状态这一步。
你也可以认为dfs和回溯是一个东西。大道至简,就像计算机你可以认为就是input,ouput。
public static void dfs(Node treeNode) {
if (treeNode == null) {
return;
}
// 遍历节点
System.out.print(treeNode.value+" ");
// 遍历左节点
dfs(treeNode.left);
// 遍历右节点
dfs(treeNode.right);
}
public static void dfsWithStack(Node root) {
if (root == null) {
return;
}
Stack<Node> stack = new Stack<>();
// 先把根节点压栈
stack.push(root);
while (!stack.isEmpty()) {
Node treeNode = stack.pop();
// 遍历节点
System.out.print(treeNode.value+" ");
// 先压右节点
if (treeNode.right != null) {
stack.push(treeNode.right);
}
// 再压左节点
if (treeNode.left != null) {
stack.push(treeNode.left);
}
}
}
//带有返回值的dfs(尾递归,不讲编译器优化,是指递归的结果直接就是函数返回值)
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if(root==null){
return false;
}
int temp = sum-root.val;
if(temp==0 && root.left==null&& root.right==null){
return true;
}
return hasPathSum(root.left,temp)|| hasPathSum(root.right,temp);
}
}
带有返回值的dfs(非尾递归)
class Solution {
private Map<Integer, Integer> indexMap;
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
// 构造哈希映射,帮助我们快速定位根节点
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < n; i++) {
indexMap.put(inorder[i], i);
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return null;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = indexMap.get(preorder[preorder_root]);
// 先把根节点建立出来
TreeNode root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
}
class Solution {
List<List<Integer>> res;
public List<List<Integer>> pathSum(TreeNode root, int sum) {
res = new ArrayList<>();//这个参数你当然可以用作参数在递归方法中传递
if(root==null){
return null;
}
backtrack(root,new ArrayList<>(),sum);
return res;
}
public void backtrack(TreeNode node,List<Integer> curData,int sum){
if(node==null){
return;
}
curData.add(node.val);
if(sum==node.val && node.left==null&&node.right==null){
res.add(new ArrayList<>(curData));
}else{
//做选择,做完之后要撤回这个操作
backtrack(node.left,curData,sum - node.val);
backtrack(node.right,curData,sum - node.val);
}
//撤销这一步的选择
curData.remove(curData.size()-1);
}
}
static List<List<Integer>> datas = new ArrayList<>();
public static void main(String[] args) {
int[] nums = new int[]{1,2,3};
new P().permute(nums);
System.out.println(datas);
}
public List<List<Integer>> permute(int[] nums) {
ArrayList<Integer> es = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
es.add(nums[i]);
}
backtrack(new ArrayList<>(), es);
return datas;
}
private void backtrack(ArrayList<Integer> curData, ArrayList<Integer> availableData) {
if (availableData.size() == 0) {
datas.add(new ArrayList<>(curData));
return;
}
//做选择
for (int i = 0; i < availableData.size(); i++) {
Integer remove = availableData.remove(i);
curData.add(remove);
backtrack(curData, availableData);
availableData.add(i, remove);
curData.remove(curData.size() - 1);
}
}
79. 单词搜索
class Solution {
public boolean exist(char[][] board, String word) {
char[] chars = word.toCharArray();
int[][] marked = new int[board.length][board[0].length];
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (board[i][j] == chars[0]) {
marked[i][j]=1;
if (selectChar(board, chars,1, i, j, marked))
return true;
}
marked[i][j]=0;
}
}
return false;
}
public boolean selectChar(char[][] board, char[] words, int index, int i, int j, int[][] marked) {
if (index >= words.length) {
return true;
}
//做各种选择,选择可以穷举出来,那就手写多个选择
if (inArea(i + 1, j, board) && board[i + 1][j] == words[index] && marked[i + 1][j] != 1) {
//如果当前可以,走下一步
marked[i + 1][j] = 1;
if (selectChar(board, words, index + 1, i + 1, j, marked)) {
return true;
}
marked[i + 1][j] = 0;
}
if (inArea(i - 1, j, board) && board[i - 1][j] == words[index] && marked[i - 1][j] != 1) {
//如果当前可以,走下一步
marked[i - 1][j] = 1;
if (selectChar(board, words, index + 1, i - 1, j, marked)) {
return true;
}
marked[i - 1][j] = 0;
}
if (inArea(i, j - 1, board) && board[i][j - 1] == words[index] && marked[i][j - 1] != 1) {
//如果当前可以,走下一步
marked[i][j - 1] = 1;
if (selectChar(board, words, index + 1, i, j - 1, marked)) {
return true;
}
marked[i][j - 1] = 0;
}
if (inArea(i, j + 1, board) && board[i][j + 1] == words[index] && marked[i][j + 1] != 1) {
//如果当前可以,走下一步
marked[i][j + 1] = 1;
if (selectChar(board, words, index + 1, i, j + 1, marked)) {
return true;
}
marked[i][j + 1] = 0;
}
return false;
}
private boolean inArea(int i, int j, char[][] board) {
return i >= 0 && j >= 0 && i < board.length && j < board[i].length;
}
}
DFS的题目,写的方法可能直接没有返回值,也可能像递归一样有返回值
回溯是 dfs 的一种表现形式。除此之外,dfs 还有另一种表现形式,它使用的是局部变量,类似于记忆;而回溯使用的是全局变量。