暂存

45 阅读2分钟

递归与迭代的区别

递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。

举个例子吧:你要给某个小孩子买玩具。
递归:
你自己不太了解小孩子的需求,为了缩小范围,让你的儿子去给孙子挑选。儿子比你强点有限,但依然不太了解小孩子的需求。为了缩小范围,你又让你孙子去挑选。如此这般,直到找到合适的玩具。**
迭代:** 你挑了一件觉得不行,又挑了一件又不行。如此这般,直到找到合适的玩具。**
所以一句话:递归是自己调用自己,每次旨在缩小问题规模。迭代是自己执行很多次,每次旨在更接近目标。**

迭代是逐渐逼近,用新值覆盖旧值,直到满足条件后结束,不保存中间值,空间利用率高。
递归是将一个问题分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈保存,问题规模较大时会占用大量内存。

二叉树

二叉树的定义

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;
    }
}

二叉树的前中后序遍历(迭代版)

前序遍历(中左右)

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> res=new ArrayList<>();
    if(root==null){
        return res;
    }
    Deque<TreeNode> stack = new LinkedList<>();  //易错点
    stack.push(root);

    while(!stack.isEmpty()){
        TreeNode temp =stack.pop();
        res.add(temp.val);

        if(temp.right!=null){  //易错点:先右边入栈,然后左边入栈,这样才能中左右的出栈
            stack.push(temp.right);
        }
        if(temp.left!=null){
            stack.push(temp.left);
        }
    }
    return res;
}

后序遍历(左右中)

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> res=new ArrayList<>();
    if(root==null){
        return res;  //易错点
    }
    Deque<TreeNode> stack = new LinkedList<>();  //易错点
    stack.push(root);

    while(!stack.isEmpty()){
        TreeNode temp =stack.pop();
        res.add(temp.val);

        if(temp.left!=null){  //易错点:先左边入栈,然后右边入栈,这样才能中右左的出栈
            stack.push(temp.left);
        }
        if(temp.right!=null){
            stack.push(temp.right);
        }
    }
    Collections.reverse(res); //易错点:中右左的出栈后要倒序
    return res;
}

中序遍历(左中右)

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Deque<TreeNode> stack = new LinkedList<>();
    TreeNode cur = root;
    while(!stack.isEmpty() || cur!=null){
        while(cur!=null){ //处理左边
            stack.push(cur);
            cur=cur.left;
        }
        cur=stack.pop();
        res.add(cur.val);
        cur=cur.right;  //处理右边
    }
    return res;
}

二叉树的深度

(那么简单我都想不到,递归办法,深度优先搜索)

public int maxDepth(TreeNode root) {
    if(root==null){
        return 0;
    }
    else{
        int m = maxDepth(root.left);
        int n = maxDepth(root.right);
        int len = m>n?m+1:n+1;
        return len;
    }
}

二叉树的重建

根据前序遍历和中序遍历重建树:再后序遍历得到后序遍历

二叉树的先序、中序、后序序列的转化

public TreeNode buildTree(int[] preorder, int[] inorder) {
    if (preorder == null || preorder.length == 0 || inorder == null || inorder.length == 0 || preorder.length != inorder.length) {
        return null;
    }
    return help(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
private TreeNode help(int[] preorder, int pStart, int pEnd, int[] inorder, int iStart, int iEnd) {
    //递归的第一步:递归终止条件,避免死循环
    if (pStart > pEnd || iStart > iEnd) {
        return null;
    }
    //重建根节点
    TreeNode treeNode = new TreeNode(preorder[pStart]);
    int index = 0;  //index找到根节点在中序遍历的位置
    while (inorder[iStart + index] != preorder[pStart]) {
        index++;
    }
    //重建左子树
    treeNode.left = help(preorder, pStart + 1, pStart + index, inorder, iStart, iStart + index - 1);
    //重建右子树
    treeNode.right = help(preorder, pStart + index + 1, pEnd, inorder, iStart + index + 1, iEnd);
    return treeNode;
}

(TODO) 根据后序遍历和中序遍历重建树:再前序遍历得到前序遍历

不能有前序和后序重建树,因为不能对左右子树进行划分。

二叉树的翻转

public static TreeNode invertTree(TreeNode root) {
    if(root == null) return null;
    TreeNode left = invertTree(root.left);
    TreeNode right = invertTree(root.right);
    root.left = right;
    root.right = left;
    return root;
}