24.常见递归,DFS,回溯题目总结

常见递归题目:
斐波那契数列
修剪二叉搜索树
重建二叉树
求二叉树的坡度
最长同值路径
二叉树的堂兄弟节点
学习递归经常告诉我们,写出递归公式就完事了。但是真实情况往往并不那么简单。

递归是一种算法结构,回溯是一种算法思想
DFS深度优先搜索是基于递归算法结构。(DFS也可以使用栈来实现)
一般就是基于递归结构来计算是否存在,搜索到和搜索不到,主要是利用栈,不保留旧的经历的路径。
回溯复杂一些,回溯的话我们一般显式的包含有撤回到原状态这一步。
你也可以认为dfs和回溯是一个东西。大道至简,就像计算机你可以认为就是input,ouput。

无返回值的dfs

public static void dfs(Node treeNode) {
        if (treeNode == null) {
            return;
        }
        // 遍历节点
        System.out.print(treeNode.value+"   ");
        // 遍历左节点
        dfs(treeNode.left);
        // 遍历右节点
        dfs(treeNode.right);
}

非基于递归的dfs


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-是否存在路径之和](https://leetcode-cn.com/problems/path-sum/)

//带有返回值的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-重建二叉树](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal

带有返回值的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;
    }
}

回溯算法-路径之和2


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 还有另一种表现形式,它使用的是局部变量,类似于记忆;而回溯使用的是全局变量。

你可能感兴趣的:(数据结构和算法)