|基本原理:
- 满二叉树 #node = 2^n -1
- 完全二叉树 底层不一定满但是一定从左往右连续的 eg : heap
- BST: 对元素顺序无所谓,对大小有要求left < root . right > root
- 平衡BST: abs(height of left - right) <=1
存储:
- 链式(LinkedList): root.left & root.right
- 线性(Array): = HEAP , 左子节点 2 * n + 1
首先,遍历的顺序: 再定义pre-order, in-order, post-order 的时候,都是根据中间结点的位置来命名。前序(pre)就是说中间结点第一个遍历,中序(in-order)就是中间结点第二个遍历,后序就是最后一个被遍历.
递归(DFS)
写递归, 有3个部分一定要确定:
- 确定返回值(二叉树中,至少遍历这块基本都是空,因为结果已经放在了全局变量res中)
- 确定终止条件: 假设走到最后一个是什么情况(最普遍的是遍历到的节点为空)
- 单层的递归逻辑, 每一次递归中需要作什么
把一个大问题变成一个小问题:
前序递归:先root + 再遍历左边所有的值 + 再遍历右边所有的值
可以写一个helper function, 每次将root的值和需要返回的List放进去
没有helper function , 唯一的不同就是为空的时候因为return value 需要返回list, 所以递归的截止条件满足的时候就return res。 因为我设置res 为全局变量,所以res整体还是不变的:
// res is global var
List<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
// return res 和 helper function下 直接return 是一样的效果
if (root == null) return res;
res.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return res;
}
中序递归:先遍历左边所有的值 + root + 再遍历右边所有的值
这里我就用具有helper function 的方法来写
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
helper(root, res);
return res;
}
public static void helper(TreeNode root, List<Integer> res){
//递归的戒截止条件
if (root == null) return ;
//先遍历左子树
helper(root.left, res);
// 中间的val
res.add(root.val);
//再遍历右子树
helper(root.right, res);
}
后序遍历:
把遍历的位置换成
先左子树
再右子树
再root
即可
迭代
递归其实就是隐形的调用了栈,所以迭代法就是把栈体现出来
in-order:
我们知道结果List的第一位肯定是最左边的Node,所以要先找到最左边的子结点,加到res里。
但是对于第一个while 循环,二刷才发现,自己理解是有偏差的,又听了卡哥的视频发现正确的思路:
往左边走到最左子节点的while循环的意义 :是为了把所有(所以才有走到空才停止的判定)的点加到stack里,为空之后就开始一个个弹出来开始进行逻辑处理。处理就是说把他的数值加到最后的结果List内,然后找他的右节点(直接让当前父节点 r = r.right 这样就可以让指针到右边的子树)
用2个循环完成, 或者一个循环,内层只是方便把左节点插入到stack里,不用也可以。
外层循环: 因为要保证栈(存储信息的工具)肯定为空,说明遍历完成。 或者当前的节点为空,就说明走到最后了,可以开始准备Pop了.
去模拟栈,具体就是模拟 以何种顺序推进栈内, 再以何种方式推出来,推出来的时候,该点的子节点会被继续推入
pre-order:
这个和后序就是一个插入顺序的事情,辅助结构Stack 因为处理的顺序和遍历的顺序一样,所以并不算很难,按照先右再左push 进去,弹出,完事儿。
post-order:
真正写的时候,发现还是不太熟悉,一直都钻牛角尖的觉得,后序应该最后是root 弹出来,但是root不先拿出来,也没有办法处理数据,也就是说先访问的肯定是root但是他不是最先处理的。那该怎么办?其实这个疑惑就是Inorder的疑惑,只不过后序这里有一个很巧妙的方法:
其实只要保证 左右中 的顺序即可 即中右左的方法输出答案,最后reverse一下即可。这个算是唯一记住的技巧。
因为栈的 FILO 特性, 所以 先放中,弹出来之后,再放左,再放右。 这样弹出来就是中之后先右后左