递归遍历
思路
递归三要素:
1、函数参数和返回值
2、终止条件
3、单层递归逻辑
前中后序指的其实是跟节点的位置,前序:根左右,中序:左根右,后序:左右根。
遍历过程中,对根的操作实际上就是 本次要得到的节点的元素值。
前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result =new ArrayList<Integer>();
preorder(root,result);
return result;
}
public void preorder(TreeNode cur,List<Integer> result){
if(cur == null) return;
result.add(cur.val);
preorder(cur.left,result);
preorder(cur.right,result);
}
}
中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result =new ArrayList<Integer>();
inorder(root,result);
return result;
}
public void inorder(TreeNode cur,List<Integer> result){
if(cur == null) return;
inorder(cur.left,result);
result.add(cur.val);
inorder(cur.right,result);
}
}
后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result =new ArrayList<Integer>();
postorder(root,result);
return result;
}
public void postorder(TreeNode cur,List<Integer> result){
if(cur == null) return;
postorder(cur.left,result);
postorder(cur.right,result);
result.add(cur.val);
}
}
迭代遍历
思路
用栈实现二叉树的前中后序遍历。
本质上递归也是用栈实现的。
从时间复杂度上其实迭代法和递归法差不多(在不考虑函数调用开销和函数调用产生的堆栈开销),但是空间复杂度上,递归开销会大一些,因为递归需要系统堆栈存参数返回值等等。
递归更容易让程序员理解,但收敛不好,容易栈溢出。
这么说吧,递归是方便了程序员,难为了机器(各种保存参数,各种进栈出栈)。
在实际项目开发的过程中我们是要尽量避免递归!因为项目代码参数、调用关系都比较复杂,不容易控制递归深度,甚至会栈溢出。
前序和后序的迭代遍历 比较好理解
前序:因为需要中左右,所以先把右压入,再把左压入,这样弹出的时候就是中左右了。
后续:中右左,所以先把左压入,再把右压入,然后翻转,就是左右中了。
在迭代的过程中:有两个操作:
1、处理:将元素放进result数组中
2、访问:遍历节点
它们要访问的元素和处理的元素顺序是一样的,都是中间节点。
当栈不为空的时候,就表示还没有访问完
而中序遍历会比较特殊,因为要访问的元素和要处理的元素顺序是不一样的,
左中右,先达到二叉树左面最底部,再处理元素。
所以需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null){
stack.push(node.right);
}
if (node.left != null){
stack.push(node.left);
}
}
return result;
}
}
后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.left != null){
stack.push(node.left);
}
if (node.right != null){
stack.push(node.right);
}
}
Collections.reverse(result);
return result;
}
}
中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()){
if (cur != null){
stack.push(cur);
cur = cur.left;
}else{
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}
}
统一迭代
思路
题解思路:
把堆节点的访问和对节点的处理分开,给要处理的中间节点后加一个null标记一起压入栈,只有遇到了null标记,才处理。这样,只要遇到了空节点,就表示下面一个节点是要处理的节点。