文章:代码随想录
状态:只会递归的遍历,迭代遍历要多重复思考
递归的写法:
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result= new ArrayList<>();
preorder(root,result);
return result;
}
private void preorder(TreeNode root, List<Integer> result) {
if (root==null){return;}
result.add(root.val);
preorder(root.left,result);
preorder(root.right,result);
}
private void inorder(TreeNode root, List<Integer> result) {
if(root==null){return;}
inorder(root.left, result);
result.add(root.val);
inorder(root.right,result);
}
private void postorder(TreeNode root, List<Integer> result) {
if(root==null){return;}
postorder(root.left,result);
postorder(root.right,result);
result.add(root.val);
}
然后介绍下统一迭代和不统一迭代的方法,代码中写明了思路:
不统一迭代:
//基本所有递归都可以通过栈实现
//前序遍历,中左右,栈是先进后出,所以要先加入右子节点才是左子节点。
//但是这里的逻辑最简单,因为过程是从上往下走的过程中就可以记录中节点的值
public class PreTraversal {
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> nodes=new Stack<>();
List<Integer> result=new ArrayList<>();
nodes.push(root);
while (!nodes.empty()){
TreeNode top = nodes.pop();
if(top==null){continue;}
result.add(top.val);
nodes.push(top.right);
nodes.push(top.left);
}
return result;
}
}
//其实这里也是需要遍历到底才能开始记录,但是可以通过翻转前序遍历的过程来实现这个结果.
//比如前序遍历是记录中左右,我们让左右节点加入栈的顺序调换,就变成了中右左.那么list记录的结果就是中右左.我们只需要将最后结果list做reverse,那么就变成了左右中。
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> nodes=new Stack<>();
List<Integer> result=new ArrayList<>();
nodes.push(root);
while (!nodes.empty()){
TreeNode top = nodes.pop();
if(top==null){continue;}
result.add(top.val);
nodes.push(top.left);
nodes.push(top.right);
}
Collections.reverse(result);
return result;
}
//中序遍历 前中后
//这里用一个指针来模拟从头指针往下遍历,当指针为空的时候就到叶子节点了。那么我们一路向左走的话,遇到第一个叶子节点就是我们要处理的值。
//将这个值pop出来记录到list中之后,再检查这个节点的右边,如果这个节点的右边也没有了那么就要继续pop,此时为上一层的父节点。这时对上一层的父节点来说左边已经pop掉了,
// 所以这时候pop的值就是中了,然后指针再变成这个节点的右节点再开始循环。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result= new ArrayList<>();
Stack<TreeNode> nodes=new Stack<>();
//初始化指针
TreeNode cur=root;
//要当指针指向为空同时栈为空时才能证明所有元素处理完了。
//因为在pop之后我们要再下一个循环才判断是否为空,那如果这时候遍历到根节点.pop掉之后,cur指向根节点的又子树,但是这时候栈空了循环就会退出,所以要同时满足两者.
while (cur!=null ||!nodes.empty()){
//如果这时候指针不为空,那么就证明当前是访问的的元素只需要记录,然后继续往左走。
if(cur!=null){
nodes.push(cur);
//指针往左走
cur=cur.left;
}else{
//当前指针为空时,那么指针的父节点就是当前需要处理的节点。
//我们同时用cur来记录这个节点
cur=nodes.pop();
//list添加结果
result.add(cur.val);
//往这个节点的右节点走,遍历右边节点
cur=cur.right;
}
}
return result;
}
统一迭代:
public class Traversal {
//我觉得着这种思路要理解空节点标记
//我认为这个null空节点标记始终标记中节点,因为只有中节点的值可以直接pop,但是左和右可以一直往下延申找到叶子节点。
// 然后每一轮(每三个节点)最后压入栈的就是遍历顺序的第一个节点。比如前序遍历最后压入栈的就是中节点,中序就是左节点。
//这个第一个节点其实我的理解就是每次往下遍历的入口,同时也是最后需要第一个pop出的元素。比如前序遍历就是顺着中节点往下走,中序遍历就是顺着左子节点一直往下遍历,后续就是从左到右最后才是中.
//这个null标记后面的元素其实可以理解为每一轮需要留下的/静止的元素.比如前序的每一轮遍历完需要把中节点放最后,因为前序遍历可以一边访问的过程一边就处理中节点,而中后序遍历就必须先沿着一条路访问到子节点才行.
//那么中序的每一轮就是栈中就是 [右 中 null 左],然后左会被不断的pop然后沿着这条路往下走直到遇到叶子节点,所以null指针也可以理解为每一轮的不变量,等左遍历完之后才轮到中
//这也是为什么null不能入栈
//前序遍历
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> st = new Stack<>();
if (root != null) st.push(root);
while (!st.empty()) {
TreeNode node = st.peek();
if (node != null) {
st.pop(); // 将该节点弹出,避免重复操作,下面再将右左中节点添加到栈中(前序遍历顺序中左右)
if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈)
if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.peek(); // 重新取出栈中元素
st.pop();
result.add(node.val); // 加入到结果集
}
}
return result;
}
//中序遍历 左中右
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> st = new Stack<>();
if (root != null) st.push(root);
while (!st.empty()) {
TreeNode node = st.peek();
if (node != null) {
st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中(中序遍历顺序左中右)
if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.peek(); // 重新取出栈中元素
st.pop();
result.add(node.val); // 加入到结果集
}
}
return result;
}
//后序遍历 左右中
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> st = new Stack<>();
if (root != null) st.push(root);
while (!st.empty()) {
TreeNode node = st.peek();
if (node != null) {
st.pop(); // 将该节点弹出,避免重复操作,下面再将中右左节点添加到栈中(中序遍历顺序左右中)
st.push(node); // 添加中节点
st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈)
if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.peek(); // 重新取出栈中元素
st.pop();
result.add(node.val); // 加入到结果集
}
}
return result;
}
}