算法刷题 Leetcode 二叉树 0118-0119

算法刷题 Leetcode 二叉树 0118-0119

各种遍历及解题模板

递归写法没什么好说的,基本功,砍瓜切菜。重点看非递归方式

144. 二叉树的前序遍历

  • 递归方式
class Solution {
private:
    vector<int> res;
public:
    void LNR(TreeNode* root){
        if(root==nullptr) return;        
        res.push_back(root->val);
        LNR(root->left);        
        LNR(root->right);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        LNR(root);
        return res;
    }
};
  • 迭代方式
vector<int> res;
stack<TreeNode*> st; 
st.push(root);
TreeNode *cur = root;
while(cur!=nullptr && !st.empty()){
    cur = st.top();                 
    res.push_back(cur->val);
    st.pop();
    if(cur->right != nullptr) st.push(cur->right);
    if(cur->left != nullptr) st.push(cur->left);       
}
return res;
  • 前序遍历模板

        stack<TreeNode*> st; 
        st.push(root);
        TreeNode *cur = root;
        if(cur == nullptr) ..为空特判
        while(!st.empty()){
            cur = st.top(); st.pop();               
            。。操作            
            if(cur->right != nullptr) st.push(cur->right);
            if(cur->left != nullptr) st.push(cur->left);       
        }

145. 二叉树的后序遍历

class Solution {
private:
    vector<int> res;
public:
    void LNR(TreeNode* root){
        if(root==nullptr) return;
        LNR(root->left);        
        LNR(root->right);
        res.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        LNR(root);
        return res;
    }
};
  • 迭代方式
    曲线救国,后序遍历顺序是左右根,倒过来是根右左,而我们已经在实现前序遍历时做到了根左右遍历,只需调换遍历两个孩子的顺序即可,所以可以先根右左遍历,再整个翻转。
class Solution {

public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st; 
        st.push(root);
        TreeNode *cur = root;
        // 中右左
        while(cur!=nullptr && !st.empty()){
            cur = st.top();                 
            res.push_back(cur->val);
            st.pop();
            if(cur->left != nullptr) st.push(cur->left);   // 先push后访问
            if(cur->right != nullptr) st.push(cur->right);                
        }
        // 翻转为左右中
        reverse(res.begin(), res.end());
        return res;
    }
};

94. 二叉树的中序遍历

  • 递归方式
class Solution {
private:
    vector<int> res;
public:
    void LNR(TreeNode* root){
        if(root==nullptr) return;
        LNR(root->left);
        res.push_back(root->val);
        LNR(root->right);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        LNR(root);
        return res;
    }
};
  • 迭代方式

    同DFS迭代方式,先一直左下,无法左下了输出结点值,再进行右下

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;
        TreeNode * cur=root;
        while(cur!=nullptr || !st.empty()){
            if(cur!=nullptr){
                st.push(cur);                
                cur = cur->left;
            }else{
                cur = st.top();
                st.pop();
                res.push_back(cur->val);
                cur =cur->right;
            }
        }
        return res;
    }
};
  • 迭代方式模板
stack<TreeNode*> st;
TreeNode * cur=root;
while(cur!=nullptr || !st.empty()){
    if(cur!=nullptr){
        st.push(cur);                
        cur = cur->left;
    }else{
        cur = st.top();
        st.pop();
        // 操作
        cur =cur->right;
    }
}

102. 二叉树的层序遍历

  • 层序遍历模板
		TreeNode *cur;
        queue<TreeNode*> q;
        if(root!=nullptr) q.push(root);
        while( !q.empty() ){
            int size = q.size();
            vector<int> layer;            
            while(size--)
            {
                cur = q.front();
                q.pop();  
                .. 进行操作
                if(cur->left != nullptr) q.push(cur->left);
                if(cur->right != nullptr) q.push(cur->right);
            }
             一层结束,进行操作
        }
  • 迭代法
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        TreeNode *cur;
        queue<TreeNode*> q;
        if(root!=nullptr) q.push(root);
        vector<vector<int>> ans;
        while( !q.empty()){
            // 按层保存结果巧妙解法,先记录下当前层元素个数,再进行处理相应个数的元素即可进入下一层
            int cnt = q.size();
            vector<int> layer;            
            while(cnt--)
            {
                cur = q.front();
                layer.push_back(cur->val);
                q.pop();                
                if(cur->left != nullptr) q.push(cur->left);
                if(cur->right != nullptr) q.push(cur->right);
            }
            ans.push_back(layer);
        }
        return ans;
    }
};

589. N 叉树的前序遍历

  • 迭代

class Solution {
public:
    vector<int> preorder(Node* root) {
        vector<int> res;
        stack<Node*> st;
        if(root != nullptr)st.push(root);
        Node* cur;
        while(!st.empty()){
            cur = st.top();
            st.pop();
            res.push_back(cur->val); // 中
            vector<Node*> children = cur->children;
            // 从后往前push(右左),栈弹出来是从左往右(左右),形成左中右遍历
            for(int i= children.size()-1; i>=0; i--){ 
                st.push(children[i]);
            }
        }
        return res;
    }
};
  • 递归
class Solution {
private:
    vector<int> res;
public:
    void visit(Node* root){
        if(root == nullptr) return;
        res.push_back(root-> val); 
        for(auto child : root->children){
            // 无须在这里push
            visit(child);
        }
    }
    vector<int> preorder(Node* root) {
        visit(root);
        return res;
    }
};

590. N 叉树的后序遍历

  • 迭代方式
    类似于二叉树的后序遍历迭代方式。官方题解的迭代方式还用到了哈希表,等二刷时再研究了。
class Solution {
public:
    vector<int> postorder(Node* root) {
        vector<int> res;
        stack<Node*> st;
        if(root != nullptr)st.push(root);
        Node* cur;
        while(!st.empty()){
            cur = st.top();
            st.pop();
            res.push_back(cur->val); // 中
            vector<Node*> children = cur->children;
            for(auto child : children){ // (根)左右
                st.push(child);
            }
        }
        reverse(res.begin(), res.end());
        return res;
    }
};
  • 递归方式

    就将前序递归稍作修改即可。

class Solution {
private:
    vector<int> res;
public:
    void visit(Node* root){
        if(root == nullptr) return;        
        for(auto child : root->children){
            // 无须在这里push
            visit(child);
        }
        res.push_back(root-> val); 
    }
    vector<int> preorder(Node* root) {
        visit(root);
        return res;
    }
};

操作二叉树

总结在前

树的操作题型归类:

  • 各种遍历的应用
    • 翻转二叉树
    • 判树型
      • 是否为对称二叉树:101题
      • 是否为平衡二叉树:110题
      • 是否为二叉排序树(二叉搜索树):98题
    • 树的属性
      • 最大深度:104、最小深度:111、节点个数:222
      • 路径相关:257、112
    • 其他:404、513、
  • 建树
    • 106/654

跟树有关的题大都是建立在遍历的基础上,也就是说,我们可以基于遍历模板根据题目要求进行解答。

226. 翻转二叉树

  • 递归法
class Solution {
public:
    void reverseTree(TreeNode* root){
        if(root==nullptr || root->left==nullptr && root->right == nullptr) return;
        reverseTree(root->left);
        reverseTree(root->right);
        
        TreeNode *tmp = root->left;
        root->left = root->right;
        root->right = tmp;
    }
    TreeNode* invertTree(TreeNode* root) {
        reverseTree(root);
        return root;
    }
};
  • 迭代法:二刷补

101. 对称二叉树

  • 迭代法
class Solution {
public:
    bool check(TreeNode* left, TreeNode* right){
        queue<TreeNode*> q; // 按层进行比较,用队列。 层序、前中后序都可,层序最简单
        q.push(left); q.push(right);
        while(!q.empty()){
            left = q.front(); q.pop();
            right = q.front(); q.pop();
            if( left == nullptr && right == nullptr ) continue; // 排除都为空的情况
            else if( left==nullptr || right==nullptr || left->val != right->val) return false; // 有一个为空或值不相等,返回false

            q.push(left->left);
            q.push(right->right);      

            q.push(left->right);            
            q.push(right->left);
        }
        return true;
    }
    bool isSymmetric(TreeNode* root) {
        return check(root, root);        
    }
};
  • 递归法
class Solution {
public:
    bool check(TreeNode* left, TreeNode* right){
        if( left == nullptr && right == nullptr ) return true;
        else if( left == nullptr || right == nullptr ) return false;
        else if( left->val != right->val ) return false;

        return check(left->left, right->right) && check(left->right, right->left);
    }
    bool isSymmetric(TreeNode* root) {
        return check(root, root);        
    }
};

104. 二叉树的最大深度

基于层序遍历,遍历完树即可得到结果。

  • 迭代法
class Solution {
public:
    int maxDepth(TreeNode* root) {
        int deepth=0;
        queue<TreeNode*> q;
        if(root != nullptr) q.push(root);
        else return 0;
        TreeNode *p ;
        while(!q.empty()){
            int size = q.size();
            while(size--){
                p = q.front();
                q.pop();
                if( p->left != nullptr ) q.push(p->left);
                if( p->right != nullptr ) q.push(p->right);
            }
            deepth++;
        }
        return deepth;
    }
};

111. 二叉树的最小深度

仍基于层序遍历,找到叶子结点返回结果即可。

  • 迭代法
class Solution {
public:
    int minDepth(TreeNode* root) {
        int deepth=0;
        queue<TreeNode*> q;
        if(root != nullptr) q.push(root);
        else return 0;
        TreeNode *p ;
        while(!q.empty()){
            int size = q.size();
            while(size--){
                p = q.front();
                q.pop();
                if( p->left == nullptr && p->right == nullptr ) return deepth+1;
                if( p->left != nullptr ) q.push(p->left);
                if( p->right != nullptr ) q.push(p->right);
            }
            deepth++;
        }
        return deepth;
    }
};

222. 完全二叉树的节点个数

  • 迭代版:不利用完全二叉树性质,二叉树统计个数通用版
    int countNodes(TreeNode* root) {
   		TreeNode *cur;
        queue<TreeNode*> q;
        if(root!=nullptr) q.push(root);
        int ans = 0;
        while( !q.empty() ){     
            cur = q.front();
            q.pop();  
            ans++;
            if(cur->left != nullptr) q.push(cur->left);
            if(cur->right != nullptr) q.push(cur->right);
        }
        return ans;
    }
  • 递归版:利用完全二叉树性质

完全二叉树的特性:1、要么为满二叉树,要么只有最后一层未满,及最后一层的结点个数为 [ 1, 2^(h-1) ];2、叶子结点在最后两层,

官方题解让人不想看,carl 的解法容易理解得多,有点平衡二叉树的左右子树都是平衡二叉树那味了。

class Solution {
public:
    int countNodes(TreeNode* root) {
   		if(root == nullptr) return 0;
        TreeNode *p = root->left;
        int leftdeepth = 0, rightdeepth = 0;
        while(p != nullptr) {
            leftdeepth++;
            p = p -> left;
        }
        p = root->right;
        while(p != nullptr) {
            rightdeepth++;
            p = p->right;
        }
        if(leftdeepth == rightdeepth) return (2<<leftdeepth) - 1;
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

110. 平衡二叉树

刚说到平衡二叉树,下一题就来了。不想会。。

257. 二叉树的所有路径

  • 递归法:前序遍历递归模型
class Solution {
private:
    vector<string> res;
public:
    void dfs(TreeNode* root, string s){ // 遍历模型:根左右
        if(root == nullptr) return;
        if(root->left == nullptr && root->right == nullptr){ // 叶子结点
            s += to_string(root->val);
            res.push_back(s);
            return;
        }
        dfs(root->left, s + to_string(root->val) + "->"); // 省得回溯
        dfs(root->right, s + to_string(root->val) + "->");
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        dfs(root, ""); 
        return res;
    }
};

404. 左叶子之和

遍历框架:左中右 中序遍历

  • 迭代法
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        // 左中右 中序遍历框架
        int sum = 0;
        stack<TreeNode*> st;
        TreeNode * cur=root;
        if(cur->left == nullptr && cur->right == nullptr) return 0;
        while(cur!=nullptr || !st.empty()){
            if(cur!=nullptr){
                st.push(cur);                
                cur = cur->left;
            }else{
                cur = st.top();
                st.pop();
                if(cur->left == nullptr && cur->right == nullptr) sum += cur->val;
                cur =cur->right;
                if(cur != nullptr && cur->left == nullptr && cur->right == nullptr)
                    cur = nullptr;  // 右叶子,这里赋空下一循环就重新取结点,continue无用
            }
        }
        return sum;
    }
};

513. 找树左下角的值

用层序模板最方便,理论上讲中序未必不行,但是这个深度我算是放弃debug了,二刷整活。

  • 迭代版
class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        TreeNode *cur;
        queue<TreeNode*> q;
        if(root!=nullptr) q.push(root);
        int ans;
        while( !q.empty() ){
            int size = q.size();
            vector<int> layer;            
            for(int i=0; i<size; i++)
            {                
                cur = q.front();
                q.pop();  
                if(i==0) ans = cur->val;
                if(cur->left != nullptr) q.push(cur->left);
                if(cur->right != nullptr) q.push(cur->right);
            }            
        }
        return ans;
    }
};

112. 路径总和

  • 前序遍历模板:递归版
class Solution {
private:
    bool res=false;
public:
    void LNR(TreeNode* root, int sum, int tar){    
        if( root == nullptr || res) return; 
        sum += root->val;   
        if( root->left == nullptr && root->right == nullptr){
            if(sum == tar ) res = true; 
            return;    
        }      
        LNR(root->left, sum, tar); 
        LNR(root->right, sum, tar);
        sum -= root->val; // 回溯
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        LNR(root, 0, targetSum);
        return res;
    }
};

106. 从中序与后序遍历序列构造二叉树

这题是建树了,不是基于树进行操作,遍历模板不能派上用场。

  • 递归法
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(postorder.size() == 0) return nullptr;
        // 后序遍历中取出根
        int rootval = postorder[postorder.size()-1];
        TreeNode *root = new TreeNode(rootval);
        // 中序遍历中找到根
        for(int i=0; i<inorder.size(); i++){
            if(inorder[i] == rootval){ // 根结点位置  
                // 建左子树              
                vector<int> leftinorder(inorder.begin(), inorder.begin() + i); // 左闭右开截取
                vector<int> leftpostorder(postorder.begin(), postorder.begin() + i);
                root ->left = buildTree(leftinorder, leftpostorder);
                // 建右子树
                vector<int> rightinorder(inorder.begin() + i + 1, inorder.end());
                postorder.resize(postorder.size()-1); // 根结点不包括
                vector<int> rightpostorder(postorder.begin()+leftinorder.size(), postorder.end()); 
                root ->right = buildTree(rightinorder, rightpostorder);  
            }
        }
        return root;
    }
};

654. 最大二叉树

这是一道建树变种,基于上一题代码改改即可。

  • 递归
class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& a) {
        if (a.size() == 0) return nullptr;
        int maxIndex = 0, max = 0;
        for(int i=0; i<a.size(); i++){
            if(a[i]>=max){
                maxIndex = i;
                max = a[i];
            }
        }
        // 建根
        TreeNode *root = new TreeNode(max);         
        // 建左子树              
        vector<int> left(a.begin(), a.begin() + maxIndex); // 左闭右开截取
        root ->left = constructMaximumBinaryTree(left);         
        // 建右子树     
        vector<int> right(a.begin() + maxIndex + 1, a.end()); 
        root ->right = constructMaximumBinaryTree(right);            
        return root;
    }
};

617. 合并二叉树

还是用建树模板即可。

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if(root1 == nullptr && root2 == nullptr) return nullptr;
        else if(root1 == nullptr) return root2;
        else if(root2 == nullptr) return root1;        
        // 都不为空
        TreeNode *root = new TreeNode(root1->val + root2->val);
        root->left = mergeTrees(root1->left, root2->left);
        root->right = mergeTrees(root1->right, root2->right);
        return root;
    }
};

98. 验证二叉搜索树

中序遍历后判断序列是否升序,否则不是二叉搜索树。

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        TreeNode* p = root;
        stack<TreeNode*> st;
        vector<int> res;
        while(p != nullptr || !st.empty()){
            if(p!=nullptr){
                st.push(p);
                p = p->left;
            }
            else{
                p = st.top(); st.pop();
                res.push_back(p->val);
                p = p->right;
            }
        }
        for(int i=1; i<res.size(); i++){
            if(res[i] <= res[i-1]) {
                return false; 
            }          
        }
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值