代码随想录算法训练营第十四天|理论基础、递归遍历、迭代遍历、统一迭代

递归遍历

题目链接:145. 二叉树的后序遍历

       说说我对代码随想录里的三要素的理解: 

1. 确定递归函数的参数和返回值:

        因为这题是要返回一个含有后序遍历出来的结果集合,肯定是先创建一个result结果集,然后把每一次按后序遍历到的元素add到这个集合中去,那么,显然“add”是要操作这个集合,所以,需要把集合作为参数传入,而遍历这棵树,树的每一个节点肯定是要的,也作为参数,故递归函数有两个参数:集合(List<Integer> result),树节点(TreeNode root)。

2. 确定终止条件:

        显然,走到节点为空时,就是终止的时候,那就是root == null时。

 

3. 确定单层递归的逻辑:

        根据1,我们确定了我们的函数是:posTraversal(TreeNode root, List<Integer> result)

单层的递归逻辑就是“往集合中加入值”,是按“前/中/后”序的要求来选择先加入哪里的值。

这里是后序,那就是先去左孩子,把左孩子节点的val加入到集合中去,但是因为左孩子可能是一个分支,是分支的话,那么又对左分支进行后序遍历,所以递归调用posTraversal,这时参数是(root.left,result)。

        同样逻辑,再去右孩子,把右孩子节点的val加入到集合中去,但是因为右孩子可能是一个分支,是分支的话,那么又对右分支进行后序遍历,所以再一次递归调用posTraversal,这时参数是(root.right,result)。

那么最后,我们再加入中间的节点,这时候不用担心了,中间的一定是节点,所以直接result.add(root.val);

最后根据三要素写出整体代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result =  new ArrayList<>();
        posTraversal(root,result);
        return result;
    }
    void posTraversal(TreeNode root, List<Integer> result) {
        if(root == null) {
            return;
        }
        posTraversal(root.left, result);
        posTraversal(root.right, result);
        result.add(root.val);
    }
}

迭代遍历

题目链接:144. 二叉树的前序遍历

题目链接:94. 二叉树的中序遍历

题目链接:145. 二叉树的后序遍历

        前序遍历的迭代法就是先让根节点入栈(如果有根节点,没有则直接返回空的result),然后当栈不为空时,循环迭代判断,用一个临时树节点temp保存栈顶元素,然后访问栈顶元素,并且出栈(这就是“前”),之后,根据temp(此时还在“前”的位置上),先判断其右孩子是否为空,不为空,则先入栈(因为前序遍历是“前左右”,而栈的弹出是从栈顶,也就是后进的才会先出,为了访问完“前”,再访问“左”,就必须后让“左”入栈,之后栈顶的就是“左”了),之后判断左孩子是否为空,不为空则将左孩子入栈。这样,一个前序遍历的迭代法就完成了。

前序遍历迭代法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> temp = new Stack<>();
        if(root == null){
            return result;
        }
        temp.push(root);
        while(!temp.isEmpty()) {
            TreeNode t = temp.pop();
            result.add(t.val);
            if(t.right != null) {
                temp.push(t.right);
            }
            if(t.left != null) {
                temp.push(t.left);
            }
            
        }
        return result;
    }
    
}

        最难的莫过中序遍历了,中序遍历也有前序遍历的影子,也有一个中间变量来辅助入栈和访问,但他更像是一个链表中的指针,叫它cur,通过指针cur的移动,不停的遍历左子树,知道左子树为空了为止,遍历左子树的同时,将遍历过的左子树上的节点都入栈,当左子树为空时,即cur为空时,让cur回到栈顶的元素上,cur第一次为空时,栈顶元素一定是这棵树最底层最左边的节点,此时访问它,并让栈顶弹出,此时新的栈顶元素就是“中”,令cur指向cur的右孩子,别忘了还有访问右孩子,可能描述起来还是有点抽象,还是得看代码,因为会涉及到弹出栈这种回溯操作,还是有点难想象的,建议画图理解吧。

中序遍历迭代法:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;    // cur所指,即为要压入栈中的节点,当cur为空时,则表示要访问(将栈顶元素加入集合中)栈顶元素并将其弹出栈,接着让cur访问右子树
        while(cur != null || !stack.isEmpty()) {
            if(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }else {
                cur = stack.peek();         // 取出栈顶,栈顶一定是刚刚入栈的“中”
                result.add(cur.val);        // 接下来访问
                stack.pop();                // 弹出栈,表示访问完毕
                cur = cur.right;            // 别忘了右子树。如果右子树为null了,说明要回溯了,那等等还会进入到else中,栈顶即是要回溯到的第一个点
            }
        }
        return result;
    }

    
}

        后续遍历就是在前序遍历的基础上修改就行,在入栈时,先让左孩子入栈再让右孩子入栈,这样就能得到一个“中右左”的结果集,然后把结果集逆转一下,就是后序遍历的“左右中”了。

后序遍历迭代法:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result =  new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        if(root == null) {
            return result;
        }
        TreeNode temp = root;
        stack.push(temp);
        while(!stack.isEmpty()) {
            temp = stack.pop();
            result.add(temp.val);
            if(temp.left != null) {
                stack.push(temp.left);
            }
            if(temp.right != null) {
                stack.push(temp.right);
            }
        }
         Collections.reverse(result);
         return result;

    }
    
}

 Collections.reverse(result):反转集合元素

统一迭代

        统一的方法直接上代码随想录链接吧,先不看了。。。

代码:

class Solution {
    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;
    }
}

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树理论基础递归遍历迭代遍历统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值