代码随想录算法训练营day14

题目:递归遍历、迭代遍历、统一迭代

参考链接:代码随想录

基础知识

满二叉树

深度为k,节点数为2^k-1,其中k从1开始

完全二叉树

除了最底层节点未满,其他层节点都满,且最底层节点集中到左边。**即从上往下从左往右遍历能连着。**最底层为k,则最底层节点数从1到2^(k-1)

二叉搜索树

树节点有元素,且对每个子树,左侧所有节点都小于根节点,右侧都大于根节点。

存储方式

数组存储和链式存储。

遍历方式

DFS:前序,中序,后序。
前序:中左右;中序:左中右;后序:左右中。
BFS:从上往下从左往右。

代码实现

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

平衡二叉搜索树AVL

两个左右子树高度相差不过1。常见的就是红黑树。

二叉树的递归遍历

144.二叉树的前序遍历
145.二叉树的后序遍历
94.二叉树的中序遍历

思路:递归写法,首先要确定几个条件,首先是结束条件和返回值,然后是递归逻辑。对于二叉树的递归遍历,相对比较简单,即节点为空则停止遍历,否则对左右子树分别递归。时间复杂度O(n)。
前序代码:

class Solution {
public:
    void preTra(vector<int> &ans,TreeNode* root){//注意递归要返回void,需要单独写个函数,否则无法写入ans中
        if(!root){
            return;
        }
        ans.push_back(root->val);
        preTra(ans,root->left);
        preTra(ans,root->right);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ans;
        preTra(ans,root);
        return ans;
    }
};

后序代码:

class Solution {
public:
    void postTra(vector<int> &ans,TreeNode* root){//注意递归要返回void,需要单独写个函数,否则无法写入ans中
        if(!root){
            return;
        }    
        postTra(ans,root->left);
        postTra(ans,root->right);
        ans.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ans;
        postTra(ans,root);
        return ans;
    }
};

中序代码:

class Solution {
public:
    void inTra(vector<int> &ans,TreeNode* root){//注意递归要返回void,需要单独写个函数,否则无法写入ans中
        if(!root){
            return;
        }    
        inTra(ans,root->left);
        ans.push_back(root->val);
        inTra(ans,root->right);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        inTra(ans,root);
        return ans;
    }
};

二叉树的迭代遍历

思路:递归可以想到使用栈来解决,我们先看前序,顺序是根->左->右,碰到root节点,首先就将其入栈,然后出栈代表遍历这个节点,对两个子节点,需要先将右节点进栈,然后再将左节点进栈。这样出栈的时候,就是先出左,再出右。根节点一开始就处理了,处理完根节点后立刻将右左进栈处理。注意根节点一开始最好入栈,这样才能根据栈不为空进行while循环。
一开始写的错误代码:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<int> st;
        if(!root){
            return ans;
        }
        st.push(root->val);
        while(!st.empty()){
            ans.push_back(st.top());//先头节点
            if(root->right)
        }
    }
};

主要原因是我将节点中的val入栈,导致再处理完根节点后不知如何处理左右节点,因为仅仅根据st头部的一个值无法找到左右节点,循环也无法进行,看完解析后发现需要将整个节点存入栈中。
标答:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> st;
        if(!root){
            return ans;
        }
        st.push(root);
        while(!st.empty()){
            TreeNode* node=st.top();
            st.pop();
            ans.push_back(node->val);//先头节点
            if(node->right){
                st.push(node->right);
            }
            if(node->left){
                st.push(node->left);
            }
        }
        return ans;
    }
};

后序为左->右->中,解决方法类似,注意我们通过栈的处理,一定是先处理的根节点,将后序反过来,就是中->右->左,只需要将前序的左右顺序一调换,然后最后将结果做一个翻转就OK。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> st;
        if(!root){
            return ans;
        }
        st.push(root);
        while(!st.empty()){
            TreeNode* node=st.top();
            st.pop();
            ans.push_back(node->val);//先头节点
            if(node->left){
                st.push(node->left);
            }
            if(node->right){
                st.push(node->right);
            }
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

最麻烦的是中序遍历,由于是左->根->右的顺序,不管是否倒转,都无法先处理根节点。所以代码会复杂一些,思路主要是使用指针用来遍历所有节点,然后栈用来存放节点。对所有子树,进栈顺序为中,左,右,注意当中节点出栈后,才能处理右节点。
一开始的错误代码:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> st;
        if(!root){
            return ans;
        }
        st.push(root);
        while(!st.empty()){
            TreeNode* node=st.top();//遍历节点
            if(node->left){//左节点不为空,一直往左遍历,入栈
                node=node->left;
                st.push(node);
            }
            else{//左节点为空,遍历中节点,出栈
                ans.push_back(node->val);
                st.pop();
                if(node->right){//右节点不为空
                    node=node->right;
                    st.push(node);
                }
            }
        }
        return ans;
    }
};

主要问题是在左节点不为空的时候,遍历完左节点后,下次访问栈顶的根节点,还是会又根据左节点不为空继续访问左节点。看完标答后,发现需要直接将cur指针完全用于遍历过程,然后在一开始就判断目前访问的节点为不为空。
标答:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> st;
        TreeNode* node=root;//遍历节点
        while(node||!st.empty()){//node不为空说明目前遍历的节点需要入栈,栈不为空说明还有节点没遍历完
            if(node){//node不为空,入栈,一直往左遍历
                st.push(node);
                node=node->left;
            }
            else{//处理栈中元素 
                node=st.top();
                ans.push_back(node->val);//记住入栈一定是目前访问的节点,也就是中节点
                st.pop();
                node=node->right;//这里不用考虑右为不为空,如果为空下次循环再处理
            }
        }
        return ans;
    }
};

总结:前序遍历中访问节点和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!

统一迭代

pass

你可能感兴趣的:(算法)