递归代码模板
# 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
一天做了10个左右的题,头晕脑胀,感觉相似的题目却又不同,所以有些题目做的很费劲,没有很好的处理,做完了效果也不是很好,并不像之前做完一个,都会有一些思路去处理,一些注意事项等等,现在感觉做完了就全凭感觉的样子,还需要冷静思考总结才行。
而且,虽然知道了这些题目都是针对递归的训练题目,但是并不是仅仅这样就够了,还需要注意的是有些题目是比较复杂的,是有逻辑需要抽象处理的,并不是知道用递归就行了,还要注意问题处理的过程,边界等。比如括号的问题,还是要了解有效括号的特性,知道如何迭代才行。
什么是有效的二叉搜索树,二叉搜索树的中序遍历特点是否可以用上等,有很多都是需要画图才能够真正解决的。但是这里条件有限,所以也别太心急,多做几遍就好了,加油。每个题都去思考对应的逻辑,处理逻辑,而不是总想一次带走,有些需要更多的逻辑处理来支撑,分析问题,找到问题的核心逻辑,找到重复点,才有助于解决问题。
同时,通过对下面的二叉树公共祖先的学习,进一步认识到了
状态空间和结果空间的区别
状态空间,是指问题本身可能有哪些情况,比如下面的分析
结果空间则是问题通过当前的解答方式可以得到的答案有哪些,比如括号组合的题,最后需要在解空间中进行过滤,得到全部合法的解,还有就是最大矩形面积,如果按照每个bar的高作为矩形的高进行枚举,则也是构建了一个解空间,在这个解空间中最终搜索只会得到一个最优解
有些问题需要对状态空间更好的划分,才更好得到解空间
而有些问题则不是构建解空间,而是直接求解得到唯一解,并不涉及最优概念,而是一个对错型的题,只有一个解,比如矩形面积,是多个解找最大的(数据线构建解空间再求解的情况),共同父节点则是直接得到最终解(没有解空间的概念)
/**
* 这个问题的分析,从网上看的思路,感觉有些很好,这里应该怎样分析
* 首先是问题的状态空间
* 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;
}
}
非常开心,这个题通过自己的探索得到了一个新的解法,而且效率也算中规中矩,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);
}
}
}