算法训练营07-递归使用练习

递归代码模板


# Python 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     处理一些状态


实战题目
爬楼梯
括号问题22
二叉树翻转226
二叉搜索树校验98 搜索树是不允许相等的元素存在的
二叉树最大深度104
二叉树最小深度111
二叉树的序列化反序列化297
每日一课
如何优雅地计算斐波那契数列

课后作业
二叉树两个节点的公共祖先236
前序遍历与中序遍历构造二叉树105
n个数中所有可能结合77
没有重复 数字的序列,返回其所有可能的全排列46
可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列47

小结 01

一天做了10个左右的题,头晕脑胀,感觉相似的题目却又不同,所以有些题目做的很费劲,没有很好的处理,做完了效果也不是很好,并不像之前做完一个,都会有一些思路去处理,一些注意事项等等,现在感觉做完了就全凭感觉的样子,还需要冷静思考总结才行。
而且,虽然知道了这些题目都是针对递归的训练题目,但是并不是仅仅这样就够了,还需要注意的是有些题目是比较复杂的,是有逻辑需要抽象处理的,并不是知道用递归就行了,还要注意问题处理的过程,边界等。比如括号的问题,还是要了解有效括号的特性,知道如何迭代才行。
什么是有效的二叉搜索树,二叉搜索树的中序遍历特点是否可以用上等,有很多都是需要画图才能够真正解决的。但是这里条件有限,所以也别太心急,多做几遍就好了,加油。每个题都去思考对应的逻辑,处理逻辑,而不是总想一次带走,有些需要更多的逻辑处理来支撑,分析问题,找到问题的核心逻辑,找到重复点,才有助于解决问题。

同时,通过对下面的二叉树公共祖先的学习,进一步认识到了
状态空间和结果空间的区别
状态空间,是指问题本身可能有哪些情况,比如下面的分析
结果空间则是问题通过当前的解答方式可以得到的答案有哪些,比如括号组合的题,最后需要在解空间中进行过滤,得到全部合法的解,还有就是最大矩形面积,如果按照每个bar的高作为矩形的高进行枚举,则也是构建了一个解空间,在这个解空间中最终搜索只会得到一个最优解

有些问题需要对状态空间更好的划分,才更好得到解空间
而有些问题则不是构建解空间,而是直接求解得到唯一解,并不涉及最优概念,而是一个对错型的题,只有一个解,比如矩形面积,是多个解找最大的(数据线构建解空间再求解的情况),共同父节点则是直接得到最终解(没有解空间的概念)

小结02 二叉树两个节点的公共祖先

/**
     * 这个问题的分析,从网上看的思路,感觉有些很好,这里应该怎样分析
     * 首先是问题的状态空间
     * 1. 两个节点是父子关系
     *      1. 找到的时候肯定是先找到parent,直接返回parent即可
     *
     * 2. 两个节点不是父子关系
     * 在某个子树中没有找到,只需要返回null
     *
     *      1. 在某个节点的左子树和右子树中都找到了,说明当前节点为target,逐层返回之
     *
     *      2. 在某个节点的其中一个子树中找到某一个节点(或则两个),返回chiled返回的节点,即代表找到了至少一个节点,
     * 如果在其他层级的遍历中无法找到节点,则说明这个子树中有两个这个节点,这个返回的非null糅合了多种情况,隐含了树中必然有两个节点的条件
     *
     *
     *
     */

可以得到这个精简的代码

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
            if(root == p || root == q || root == null){
                return root;
            }
            TreeNode left = lowestCommonAncestor(root.left,p,q);
            TreeNode right = lowestCommonAncestor(root.right,p,q);
            if((left != null)&&(right != null)){
                return root;
            }
            if (left != null) {
                return left;
            } else {
                return right;
            }
        }

小结03 可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列47

非常开心,这个题通过自己的探索得到了一个新的解法,而且效率也算中规中矩,60%以上
这个是根据不重复的全排推出来的,在每个迭代中增加了一个map,如果有重复的就不交换位置了

 class Solution {

        List> res = new LinkedList<>();

        public List> permuteUnique(int[] nums) {
            List numList = new LinkedList<>();
            for (int i : nums) {
                numList.add(i);
            }
            unique(numList, 0, nums.length);
            return res;

        }

        private void unique(List numList, int cur, int len) {
            if (cur == len - 1) {
                res.add(new ArrayList<>(numList));
                return;
            }
            // 这里增加了一个map用来判断,就是没法重复用,有点可惜
            Map dic = new HashMap<>();
            for (int i = cur; i < len; i++) {
                if (dic.get(numList.get(i)) != null) {
                    continue;
                }
                Collections.swap(numList, i, cur);
                unique(numList, cur + 1, len);
                Collections.swap(numList, i, cur);
                dic.put(numList.get(i), Boolean.TRUE);
            }
        }
    }

更好的答案应该是体统了更好的时间复杂度,节约了不少时间,但是思路不太一样

class Solution {
    boolean[] vis;

    public List> permuteUnique(int[] nums) {
        List> ans = new ArrayList>();
        List perm = new ArrayList();
        vis = new boolean[nums.length];
        Arrays.sort(nums);
        backtrack(nums, ans, 0, perm);
        return ans;
    }

    public void backtrack(int[] nums, List> ans, int idx, List perm) {
        if (idx == nums.length) {
            ans.add(new ArrayList(perm));
            return;
        }
        for (int i = 0; i < nums.length; ++i) {
            if (vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])) {
                continue;
            }
            perm.add(nums[i]);
            vis[i] = true;
            backtrack(nums, ans, idx + 1, perm);
            vis[i] = false;
            perm.remove(idx);
        }
    }
}

你可能感兴趣的:(算法)