LeetCode-二叉树篇

目录

  • 101.对称二叉树
  • 104.二叉树最大深度
  • 100.相同树
  • 107. 二叉树的层次遍历 II
  • 108.将有序数组转换为二叉搜索树
  • 110.平衡二叉树
  • 111.二叉树的最小深度
  • 257.二叉树的所有路径
  • 226.反转二叉树
  • 112.路径总和
  • 404.左叶子之和
  • 235.二叉搜索树的最近公共祖先
  • 199.二叉树的右视图
  • 96.不同的二叉搜索树
  • 95.不同的二叉搜索树II
  • 98.验证二叉搜索树
  • 105.从前序与中序遍历序列构造二叉树
  • 106.从中序与后续遍历序列构造二叉树
  • 109.有序链表转换二叉搜索树
  • 113.路径总和II
  • 114.二叉树展开为链表
  • 116.填充每个节点的下一个右侧节点指针
  • 129.求跟到叶子节点数之和
  • 222.完全二叉树的节点个数
  • 250.统计同值子树
  • 236.二叉树的最近公共祖先
  • 230.二叉树中第k小的元素
  • 285.二叉搜索树种的顺序后继
  • 298.二叉树最长连续序列
  • 333.最大BST子树
  • 366.寻找二叉树的叶子结点
  • 426.将二叉搜索树转化为排序的双向链表
  • 449.序列化和反序列化二叉搜索树
  • 450.删除二叉搜索树中的节点
  • 508.出现次数最多的子树元素和

101.对称二叉树

给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
   1
   /  \
  2     2
  /  \   /   \
3    4 4    3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
  1
 /  \
2    2
 \     \
 3     3

思路
DFS法1:镜像则是当前节点值相同,每个树的左子树与另一个树的右子树相同,右子树与左子树相同
BFS法2:一层一层收集,然后比较头尾是否相等

bool get_ans(TreeNode* left, TreeNode* right) {
        if (!left && !right) {
            return true;
        } else if (!left || !right) {
            return false;
        }
        return (left->val == right->val) 
        	   && get_ans(left->right, right->left)
         	   && get_ans(left->left, right->right);
    }
    bool isSymmetric(TreeNode* root) {
       return  get_ans(root, root);
}

bool isSymmetric(TreeNode* root) {
       if (!root) return true;
       deque<TreeNode *> queue;
       //先等头
       queue.push_back(root);
       while (!queue.empty()) {
           int size = queue.size();
           vector<int> vt;
           while(size--) {
              root = queue.front();
              queue.pop_front();
              //如果root是空 则随便插入一个值
              vt.push_back(root ? root->val : INT_MIN);
              if (root) {
                queue.push_back(root->left);
                queue.push_back(root->right);  
              }
             
           }
           for (int i = 0 ; i < vt.size()/2; i++) {
               if (vt[i] != vt[vt.size() - 1 - i]) {
                   return false;
               } 
           }
       }
       return true;

    }  

104.二叉树最大深度

给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
在这里插入图片描述
思路
1.BFS : 利用队列,每遍历一层,就把层数+1
2.DFS : 利用递归,每递归一次 层数 + 1

//BFS
int maxDepth(TreeNode* root) {
        if (!root) return 0;
        deque<TreeNode *> deque;
        deque.push_back(root);
        int ans = 0;
        while (!deque.empty()) {
            int size = deque.size();
            TreeNode *temp = nullptr;
            //每层遍历
            while (size--) {
                temp = deque.front();
                deque.pop_front();
                if (temp->left) deque.push_back(temp->left);
                if (temp->right) deque.push_back(temp->right);
            }
            //层数+1
            ans++;
        }
        return ans;
    }
 //DFS
    int maxDepth(TreeNode* root) {
        if (!root) return 0;
        return 1 + max(maxDepth(root->left), maxDepth(root->right));
    }

100.相同树

给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
LeetCode-二叉树篇_第1张图片
思路
方法1: DFS
先判断当前节点是否相等或者都是null 不是return false 是则继续左右递归
方法2:BFS
每层元素加入队列,拿出每层元素,比较

//DFS
 bool isSameTree(TreeNode* p, TreeNode* q) {
    if( !p && !q) return true;
    if (!p || !q) return false;
    if (p->val != q->val) return false;
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}

//BFS
bool isSameTree(TreeNode* p, TreeNode* q) {
    deque<TreeNode* > dq_p;
    deque<TreeNode* > dq_q;
    dq_p.push_back(p);
    dq_q.push_back(q);
    while (!dq_p.empty() && !dq_q.empty()) {
        int p_size = dq_p.size();
        int q_size = dq_q.size();
        if (p_size != q_size) return false;
        while (p_size--) {
        	//将每层的取出,进行比较,并且左右子树进队列
            TreeNode * temp_p = dq_p.front(); dq_p.pop_front();
            TreeNode * temp_q = dq_q.front(); dq_q.pop_front();
            if ((temp_p && !temp_q) || (!temp_p && temp_q) || (temp_p && temp_q && temp_p->val != temp_q->val)) {
                return false;
            } else if (!temp_p && !temp_q) {
                continue;
            }

            dq_p.push_back(temp_p->left);
            dq_p.push_back(temp_p->right);

            dq_q.push_back(temp_q->left);
            dq_q.push_back(temp_q->right);
        }

    }
    return true;
}

107. 二叉树的层次遍历 II

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
LeetCode-二叉树篇_第2张图片
返回其自底向上的层次遍历为:
LeetCode-二叉树篇_第3张图片
思路
逐层遍历,将每层添加到vector中,然后将ans反转一下

vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> ans;
        if(!root) return ans;
        deque<TreeNode *> dq;
        dq.push_back(root);
        while (!dq.empty()) {
            int size = dq.size();
            vector<int> vt;
            while (size--) {
                TreeNode * temp = dq.front();
                dq.pop_front();
                vt.push_back(temp->val);
                if (temp->left) dq.push_back(temp->left);
                if (temp->right) dq.push_back(temp->right);
            }    
            ans.push_back(vt);
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }

108.将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
LeetCode-二叉树篇_第4张图片
思路
因为是升序的,那么二叉搜索树的中序遍历就是升序
由于需要是平衡的,那么左右子树高度不能>1, 所以选择中间节点作为root

TreeNode * help(vector<int> & nums, int left, int right) {
        if (left > right) return nullptr;
        //奇数情况以中间作为root, 偶数情况以左边的作为节点
        int root = left + (right - left) / 2;
        TreeNode *rootTree = new TreeNode(nums[root]);
        //递归左右子树
        rootTree->left = help(nums,left, root - 1);
        rootTree->right = help(nums, root + 1, right);
        return rootTree;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return help(nums, 0 , nums.size() - 1);
    }

110.平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

思路
自顶向下: 计算左右子树高度,然后算差值,高度差>1则返回false
如果高度差<=1 那么递归左右节点, 逐个计算高度
自底向上:先序对二叉树做先序遍历,从底至顶返回子树最大高度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。
两者区别
自顶向下调用n次主函数,自底向上调用1次主函数

//自顶向下 由于需要计算所有的节点,过于浪费 推荐用自底向上
int help (TreeNode *root) {
        if (!root) return 0;
        int leftH = 1 + help(root->left);
        int rightH = 1 + help(root->right);
        //计算左右子树高度,然后返回最大值
        return max(leftH, rightH);
    }
    bool isBalanced(TreeNode* root) {
        if (!root) return true;
        //计算高度差
        int H = help(root->left) - help(root->right);
        H = abs(H);
        //递归,计算左右孩子为节点的时候的高度
        return H <= 1 && isBalanced(root->left) && isBalanced(root->right);
} 

//自底向上
int help(TreeNode * root) {
        if (!root) return 0;
        //先进行先序递归,从最左个节点开始计算
        int leftH = help(root->left);
        int rightH = help(root->right);
    //高度差>1
   //当左(右)子树高度 left== -1 时,代表此子树的 左(右)子树 不是平衡树,因此直接返回 -1 ;
        if (abs(leftH - rightH) > 1 || leftH == -1 || rightH == -1 ) {
            return -1;
        }
        //向上返回高度,当前层加上左右max高度
        return 1 + max(leftH, rightH);
    }
bool isBalanced(TreeNode* root) {
   return help(root) >= 0;     
}

111.二叉树的最小深度

给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

思路
叶节点 是表示左右孩子都为null

int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        //叶节点是左右为null 此时才返回1
        if (root->left == nullptr&& root->right == nullptr) return 1;
        int leftH = minDepth(root->left);
        int rightH = minDepth(root->right);
        //左右子树中有一个null的话 则leftH或rightH必有一个是0 
        //那么最小的深度必定是另一个非null的子树,如果left是null则最低是在right这边算起
        //相加返回
        if (root->left == nullptr|| root->right == nullptr) return leftH + rightH + 1;

        return 1 + min(leftH, rightH);
    }

257.二叉树的所有路径

给定一个二叉树,返回所有从根节点到叶子节点的路径。说明: 叶子节点是指没有子节点的节点。
LeetCode-二叉树篇_第5张图片
思路
递归: 遇到叶子节点,添加到ans中, 非叶子节点进行左右递归,路径string通过传值拷贝
栈 :栈元素为pair, 叶节点则添加second到ans中

// 递归法
void help (TreeNode * root, vector<string>  &ans, string path) {
        if (!root) return ;
        path += to_string(root->val);
        //遇到叶节点
        if (root->left == nullptr && root->right == nullptr) {
            ans.push_back(path);
        } else {
            path +=  "->";
            help(root->left, ans, path);
            help(root->right, ans, path);
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> ans;
        help(root, ans, string(""));
        return ans;
    }
// 使用栈
vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> ans;
        if (!root) return ans;
        stack<pair<TreeNode *,string>> st;
        st.push(pair<TreeNode *, string>(root,to_string(root->val)));
        while (!st.empty()) {
            pair<TreeNode *, string> temp = st.top(); st.pop();
            // 先进右 再进左
            if (temp.first->right != nullptr) {
                st.push(pair<TreeNode *, string>(temp.first->right, temp.second + "->" + to_string(temp.first->right->val)));
            }
            if (temp.first->left != nullptr) {
                st.push(pair<TreeNode *, string>(temp.first->left, temp.second + "->" + to_string(temp.first->left->val)));
            }
            // 叶子结点
            if (temp.first->left == nullptr && temp.first->right == nullptr) {
                ans.push_back(temp.second);
            }
        }
        return ans;
    }

226.反转二叉树

TreeNode* invertTree(TreeNode* root) {
      if (!root) return root;
      TreeNode *temp = root->left;
      root->left = root->right;
      root->right = temp;
      invertTree(root->left);
      invertTree(root->right);
      return root;
} 
// BFS 遍历每个节点 然后交换
TreeNode* invertTree(TreeNode* root) {
    if (!root) return root;
    deque<TreeNode *> dq;
    dq.push_back(root);
    while (!dq.empty()) {
        int size = dq.size();
        while (size--) {
            TreeNode *Node = dq.front(); dq.pop_front();
            TreeNode *temp = Node->left;
            Node->left = Node->right;
            Node->right = temp;
            if (Node->left) dq.push_back(Node->left);
            if (Node->right) dq.push_back(Node->right);
        }
    }
    return root;
}

112.路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
LeetCode-二叉树篇_第6张图片
思路
递归:每个一个节点,sum-当前节点val, 当到叶子结点的时候,判断sum是否为0
利用栈,同理

// 递归
    bool hasPathSum(TreeNode* root, int sum) {
        if (root) {
            sum -= root->val;
            if (!root->left && !root->right && sum == 0) return true;
            bool left_ans = false;
            bool right_ans = false;
            if (root->left){
                left_ans = hasPathSum(root->left, sum);    
            }
            if (root->right){
                right_ans = hasPathSum(root->right, sum);    
            }
            if (left_ans || right_ans) {
             return true;
            }
        }
        return false;
    } 
// 栈
bool hasPathSum(TreeNode* root, int sum) {
    if (!root) return false;
    stack<pair<TreeNode *,int>> st;
    st.push(pair(root,sum-root->val));
    while (!st.empty()) {
        auto temp = st.top(); st.pop();
        if (!temp.first->left && !temp.first->right && temp.second == 0) {
            return true;
        }
        if (temp.first->right) {
            st.push(pair(temp.first->right,temp.second - temp.first->right->val));
        }
        if (temp.first->left) {
            st.push(pair(temp.first->left,temp.second - temp.first->left->val));
        }
    }
    return false;
}

404.左叶子之和

计算给定二叉树的所有左叶子之和。
LeetCode-二叉树篇_第7张图片

思路
递归 : 遍历一遍,当左儿子是左子叶就添加
迭代: 同上

// 递归
int sumOfLeftLeaves(TreeNode* root) {
        if (!root  || (!root->left && !root->right) ) return 0;
        int ans = 0;
        // 左儿子非空情况下,如果左儿子是子叶就添加
        if (root->left) {
            if (!root->left->left && !root->left->right) {
                ans += root->left->val;
            }
            ans += sumOfLeftLeaves(root->left);
        }
        if (root->right) {
            ans += sumOfLeftLeaves(root->right);
        }
        return ans;
    }
//迭代
int sumOfLeftLeaves(TreeNode* root) {
    int ans = 0;
    if (!root) return ans;
    deque<TreeNode *> st;
    st.push_back(root);
    while (!st.empty()) {
        auto temp = st.front(); st.pop_front();
        // 左儿子非空情况下,如果左儿子是子叶就添加
        if (temp->left) {
            if (!temp->left->left && !temp->left->right) {
                ans += temp->left->val;
            }
            st.push_back(temp->left);
        }
        if (temp->right) {
            st.push_back(temp->right);
        }
    }
    return ans;
}

235.二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
LeetCode-二叉树篇_第8张图片

思路
从根节点开始遍历树
如果root < 节点 pp 和节点 qq ,则 pp,qq都在右边,那么以右孩子为根节点继续 1 的操作
如果root > 节点 pp 和节点 qq ,则pp,qq 都在左边,那么以左孩子为根节点继续 1 的操作
如果条件 2 和条件 3 都不成立,这就意味着我们已经找到节 pp 和节点 qq 的 LCA 了

//递归
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root) {
            int parent_val = root->val;
            int p_val = p->val;
            int q_val = q->val;
            //如果root更大,那么说明子节点在左边
            if (parent_val > p->val && parent_val > q->val) {
                return lowestCommonAncestor(root->left, p, q);
            } else if (parent_val < p->val && parent_val < q->val) {
                return lowestCommonAncestor(root->right, p, q);
            } else {
                return root;
            }
        }
        return root;
    }
// 迭代   
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
   int p_val = p->val;
   int q_val = q->val;
   while(root) {
       int parent_val = root->val;
       //如果root更大,那么说明子节点在左边
       if (parent_val > p->val && parent_val > q->val) {
           root = root->left;
       } else if (parent_val < p->val && parent_val < q->val) {
           root = root->right;
       } else {
           return root;
       }
   }
   return nullptr;
}

199.二叉树的右视图

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
LeetCode-二叉树篇_第9张图片

思路
BFS : 利用队列,收集每层元素,然后将最右边的元素添加到ans中
DFS : 先递归右子树,然后再左子树, 当depth深度值与ans.size相同的时候,添加,因为是先右所以添加的必是最右边元素

//BFS
vector<int> rightSideView(TreeNode* root) {
     vector<int> ans;
     if (!root) return ans;
     deque<TreeNode *> dq;
     dq.push_back(root);
     while (!dq.empty()) {
         int size = dq.size();
         //最右边元素,添加
         ans.push_back(dq.back()->val);
         //收集每层元素
         while (size--) {
             auto temp = dq.front(); dq.pop_front();
             if (temp->left) {
                 dq.push_back(temp->left);
             }
             if (temp->right) {
                 dq.push_back(temp->right);
             }
         }
     }
     return ans;
 } 
//DFS
void help(TreeNode * root, int depth, vector<int> &ans) {
    if (!root) return ;
    //添加
    if (depth == ans.size()) {
        ans.push_back(root->val);
    }
    //先右后左,添加的都是最右元素
    help(root->right,depth + 1, ans);
    help(root->left,depth + 1, ans);
}
vector<int> rightSideView(TreeNode* root) {
    vector<int> ans;
    help(root, 0, ans);
    return ans;
}

96.不同的二叉搜索树

给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种? LeetCode-二叉树篇_第10张图片

思路
动态规划
给定一个有序序列 1 … n,为了根据序列构建一棵二叉搜索树。我们可以遍历每个数字 i,将该数字作为树根,1 … (i-1) 序列将成为左子树,(i+1) … n 序列将成为右子树。
有n个数,那么有几种情况呢
第1个数做根节点,左子树为0个数,右子树为后n-1个数,dp[0]*dp[n-1]种

第i个数做根节点,左子树为前i-1个数,右子树为后n-i个数,dp[i-1]*dp[n-i]种

第n-1个数做根节点,左子树为前n-2个数,右子树为第n个数,dp[n-2]*dp[1]种

第n个数做根节点,前n-1个数形成其左子树,右子树为0个数,dp[n-1]*dp[0]种
我们将所有情况的二叉搜索树加起来即可

int numTrees(int n) {
    vector<int> dp(n + 1, 0);
    dp[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            dp[i] += dp[j - 1] * dp[i - j];
        }
    }
    return dp[n];
}

95.不同的二叉搜索树II

给定一个整数 n,生成所有由 1 … n 为节点所组成的 二叉搜索树 。
LeetCode-二叉树篇_第11张图片
思路
递归: 从序列 1 …n 中取出数字 i,作为当前树的树根。于是,剩余 i - 1 个元素可用于左子树,n - i 个元素用于右子树。
对序列 1 … i - 1 重复上述过程,以构建所有的左子树;然后对 i + 1 … n 重复,以构建所有的右子树。
我们就有了树根 i 和可能的左子树、右子树的列表。最后一步,对两个列表循环,将左子树和右子树连接在根上。

vector<TreeNode *> help(int start, int end) {
       vector<TreeNode *> ans;
       if (start > end) {
           return {nullptr};
       }
       for (int i = start ; i <= end; i++) {
           //获得左子树所有可能性列表
           auto left_trees = help(start, i - 1);
           //获得右子树所有可能性列表
           auto right_trees = help(i + 1, end);
           //两个列表有left * right种组合方式
           for (auto left : left_trees) {
               for (auto right : right_trees) {
                   ans.push_back(new TreeNode(i,left,right));   
               }
           }
        }
         return ans;
     }
vector<TreeNode*> generateTrees(int n) {
    if (n == 0) return {};
       return help(1, n);
 }

98.验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

思路
二叉搜索树的中序遍历是从小到大排序,那么只需要中序遍历的时候,与上一个节点比较,如果比上一个节点小,则return false;

	long long pre = LONG_MIN;
    bool isValidBST(TreeNode* root) {
        if (root) {
           //判断左子树
            if (!isValidBST(root->left)) {
                return false;
            }
            //当前节点小于前一个节点,则不是二叉搜索树
            if (root->val <= pre) {
                return false;
            }
            pre = root->val;
            //判断右子树
            if (!isValidBST(root->right)) {
                return false;
            }
        }

        return true;
    }

105.从前序与中序遍历序列构造二叉树

LeetCode-二叉树篇_第12张图片
思路
前序遍历的第一个元素,就是root元素, 然后根据这个root元素再中序遍历序列中寻找,然后分左右子树

TreeNode* help(unordered_map<int, int> & map_indes, vector<int>& preorder, int pre_start, int pre_end, vector<int>& inorder, int i_start, int i_end) {
    if (pre_start > pre_end) return nullptr;
    //根据前序的值到map中获取中序列表中这个值的index
    int in_root_index = map_indes[preorder[pre_start]];
    //左子树长度
    int left_tree_size = in_root_index - i_start;
    TreeNode *root = new TreeNode(inorder[in_root_index]);
    //左子树前序start是先序start+1,因为先序start是root,那么后一个就是左子树开始
    int left_new_p_start = pre_start + 1;
    //左子树end是先序start + 左子树size
    int left_new_p_end = pre_start + left_tree_size;
    //左子树end是中序root_index - 1
    int left_new_i_end = in_root_index - 1;
    //右子树先序start是左子树先序end + 1
    int right_new_pre_start = left_new_p_end + 1;
    //右子树中序start是root_index + 1
    int right_new_i_start = in_root_index + 1;

    root->left = help(map_indes, preorder, left_new_p_start, left_new_p_end, inorder, i_start, left_new_i_end);
    root->right = help(map_indes, preorder, right_new_pre_start, pre_end, inorder, right_new_i_start, pre_end);
    return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    int len = preorder.size();
    //用map来记录中序值对应的index, 这样就可以通过前序的index快速定位root来区分左右子树
    unordered_map<int, int> map_indes(len);
    for (int i = 0; i < len; i++) {
        map_indes[inorder[i]] = i;
    }
    return help(map_indes, preorder, 0, len - 1, inorder, 0, len - 1);
}

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

LeetCode-二叉树篇_第13张图片
思路
后序的最后一个就是节点, 得到这个节点在中序中寻找,即可氛围左右子树

TreeNode* help(unordered_map<int, int> &map, vector<int>& inorder, int in_start, int in_end,vector<int>& postorder, int p_start, int p_end){
        if (in_start > in_end || p_start > p_end) {
            return nullptr;
        }
        //在中序中获取root节点, 左右两侧则是左右子树
        int in_root_index = map[postorder[p_end]];
		//左树end
        int in_left_tree_end = in_root_index - 1;
        //右树start
        int in_right_tree_start = in_root_index + 1;

        int left_tree_size = in_root_index - in_start;

        int p_left_tree_end = p_start + left_tree_size - 1;
        int p_right_tree_start = p_start + left_tree_size;
        int p_right_tree_end = p_end - 1;

        TreeNode *root = new TreeNode(inorder[in_root_index]);
        root->left = help(map, inorder,in_start, in_left_tree_end, postorder, p_start, p_left_tree_end);
        root->right = help(map, inorder, in_right_tree_start, in_end, postorder, p_right_tree_start, p_right_tree_end);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n = inorder.size();
        unordered_map<int, int> inorder_index_map;
        for (int i = 0 ; i < n; i++) {
            inorder_index_map[inorder[i]] = i;
        }
        return help(inorder_index_map, inorder, 0 , n - 1, postorder, 0 , n - 1);
    }

109.有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
LeetCode-二叉树篇_第14张图片

思路
找mid然后分左右子树

TreeNode* help(ListNode *head, ListNode *tail) {
        if (head == tail) return nullptr;
        ListNode* slow = head;
        ListNode* fast = head;
        //快慢指针找到尾部
        while (fast->next != tail && fast->next->next != tail) {
            slow = slow->next;
            fast = fast->next->next;
        }
        TreeNode* root = new TreeNode(slow->val);
        root->left = help(head, slow);
        root->right = help(slow->next, tail);
        return root;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        return help(head, nullptr);
    }

113.路径总和II

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
LeetCode-二叉树篇_第15张图片

思路
每过一个节点 sum - root->val, 当new_sum == 0的时候添加到ans

void help(vector<vector<int>> &ans, vector<int> &vt,TreeNode *root, int sum) {
        if (!root) return ;
        vt.push_back(root->val);
        int new_sum = sum - root->val;
        if (!root->left && !root->right && new_sum == 0) {
            ans.push_back(vt);
            return ;
        } 
        if (root->left) {
           help(ans,vt,root->left,new_sum);   
           //回溯, 将root->left去掉,然后进入下面的root->right
           vt.pop_back();
        }
        
        if (root->right) {
           help(ans,vt,root->right,new_sum);
           vt.pop_back();
        }
        
    }
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        vector<vector<int>> ans;
        vector<int> vt;
        help(ans, vt, root, sum);
        return ans;
    }

114.二叉树展开为链表

给定一个二叉树,原地将它展开为一个单链表。
LeetCode-二叉树篇_第16张图片

思路
寻找左子最右个元素,然后将root->right放在这个元素的right, 然后将root->right改为左子

void flatten(TreeNode* root) {
        if (!root) return;
        auto node = root;
        while(node) {
            if(node->left) {
                auto temp = node->left;
                //寻找最右
                while(temp->right) temp = temp->right;
                //右子树换到上面最右的right
                //将左子树变为右子树
                temp->right = node->right;
                node->right = node->left;
                node->left = NULL;
            }
                
                node = node->right;
            
        }
    }

116.填充每个节点的下一个右侧节点指针

给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
 int val;
 Node *left;
 Node *right;
 Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。

LeetCode-二叉树篇_第17张图片

思路
递归 : 将左子->next = 右子, 左子的右子 = 左子->next的左子
广度优先 : 取出每层,然后逐个next队列front


      Node* connect(Node* root) {
        if (!root) return nullptr;
        if (root->left) root->left->next = root->right;
        //如果该节点有next
        if(root->next) {
            root->right->next = root->next->left;
        } 
        connect(root->left);
        connect(root->right);
        return root;
    }

     Node* connect(Node* root) {
        if (!root) return nullptr;
        deque<Node*> dq;
        dq.push_back(root);
        while (!dq.empty()) {
            int size = dq.size();
            while(size--) {
                auto front = dq.front(); dq.pop_front();
                //队列最后一个元素不需要有next
                if (size != 0)  {
                    front->next = dq.front();
                }
                if (front->left) {
                    dq.push_back(front->left);
                }
                if (front->right){
                    dq.push_back(front->right);
                }
            }
        }
        return root;
     }

129.求跟到叶子节点数之和

给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。
LeetCode-二叉树篇_第18张图片

思路
递归: 计算到该层后自顶向下的值,然后判断是否到了叶子结点
BFS: 用pair 记录到每个节点的值, 然后到叶子结点后添加到ans

void help(TreeNode *root, int num, int &ans) {
      if (!root) return ;
      //到了叶子结点 添加到ans中
      if (!root->left && !root->right) {
          num = num * 10 + root->val;
          ans += num;
          return ;
      }
      //加上当前层的数 传递到下一层
      num = num * 10 + root->val;
      help(root->left,num  , ans);
      help(root->right, num  , ans);
  }
  int sumNumbers(TreeNode* root) {
      int ans = 0;
      help(root, 0, ans);
      return ans;
  } 

  int sumNumbers(TreeNode* root) {
      if (!root) return 0;
      deque<pair<TreeNode *,int>> dq;
      //每个节点都有当前节点指针和自顶向下经过计算的值,
      dq.push_back(pair<TreeNode *,int>{root,root->val});
      int ans = 0;
      while (!dq.empty()) {
          auto front_node = dq.front(); dq.pop_front();
          //到达叶子结点后 直接取出当前节点的second值
          if (!front_node.first->left && !front_node.first->right) {
              ans += front_node.second ;
          }
         
          if (front_node.first->left) {
              dq.push_back(pair<TreeNode *,int>{front_node.first->left, front_node.second * 10 + front_node.first->left->val});
          }
          if (front_node.first->right) {
              dq.push_back(pair<TreeNode *,int>{front_node.first->right, front_node.second * 10 + front_node.first->right->val});
          }

      }
      return ans;
  } 

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

给出一个完全二叉树,求出该树的节点个数。
说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
LeetCode-二叉树篇_第19张图片

思路
方法1 : 直接计算所有个数,BFS或DFS
方法2 : 利用完全二叉树的特性,左子树h >= 右子树h, 如果左子树高度>右子树,那么右子树是完全的直接2^(h-1)计算 , 如果左子树=右子树, 那么左子树是完全的

//DFS
 int countNodes(TreeNode* root) {
        if (!root) return 0;
        return 1 + countNodes(root->left) + countNodes(root->right);
} 

//完全二叉树特性
int get_dep(TreeNode * root) {
        auto temp = root;
        int dep = 0;
        while (temp) {
            temp = temp->left;
            dep++;
        }
        return dep;
    }

 int countNodes(TreeNode* root) {
     if (!root) return 0;
     int left_dep = get_dep(root->left);
     int right_dep = get_dep(root->right);
     int ans = 0;
     //左右深度一样的情况下那么 左子树必然是完全的, 右子树非完全,利用2^dep 计算包含root节点和左子树的节点数量
     if (left_dep == right_dep) {
         ans += (1 << left_dep) + countNodes(root->right);
     } else {  //此时必是左子树深度 > 右子树深度, 同上
         ans += (1 << right_dep) + countNodes(root->left);
     }
     return ans;
 }

250.统计同值子树

给定一个二叉树,统计该二叉树数值相同的子树个数。
同值子树是指该子树的所有节点都拥有相同的数值。
LeetCode-二叉树篇_第20张图片

思路
DFS: 先从左下角的子树开始判断

 int countUnivalSubtrees(TreeNode* root) {
        int res = 0;
        helper(root, res);
        return res;
    }
    
 bool helper(TreeNode* root, int& res) {
     if(!root) return true;
     bool left = helper(root->left, res);
     bool right = helper(root->right, res);
     //自下至上, 有一个节点不和root相同都是false
     if(root->left && root->left->val != root->val || 
        root->right && root->right->val != root->val)
         return false;
     //左右元素都相同的情况
     if(left && right) res += 1;
     return true;
 }

236.二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
LeetCode-二叉树篇_第21张图片

思路
(1) 如果当前结点 root 等于 NULL,则直接返回 NULL
(2) 如果 root 等于 p 或者 q ,那这棵树一定返回 p 或者 q
(3) 然后递归左右子树,因为是递归,使用函数后可认为左右子树已经算出结果,用 left 和 right 表示
(4) 此时若left为空,那最终结果只要看 right;若 right 为空,那最终结果只要看 left
(5) 如果 left 和 right 都非空,因为只给了 p 和 q 两个结点,都非空,说明一边一个,因此 root 是他们的最近公共祖先
(6) 如果 left 和 right 都为空,则返回空(其实已经包含在前面的情况中了)

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL)
            return NULL;
        if(root == p || root == q) 
            return root;
        TreeNode* left =  lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        //左右中返回寻找到的那个
        if(left == NULL)
            return right;
        if(right == NULL)
            return left;     
        if(left && right) // p和q在两侧
            return root;
        return NULL; // 必须有返回值
    }

230.二叉树中第k小的元素

给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
LeetCode-二叉树篇_第22张图片
思路
中序: 二叉搜索树中序遍历是升序,那么收集所有元素 [K-1] 就是第K大
DFS:自底向上,没进过节点k–, 当0的时候就返回

//自底向上
 TreeNode * help(TreeNode * root, int  &k) {
        if (!root || k == 0) return root;
        auto t = help(root->left,k);
        if (t) return t;
        k--;
        if (k == 0) {
            return root;
        }
        return help(root->right, k);
}
int kthSmallest(TreeNode* root, int k) {
    return help(root,k)->val;
} 
  
//利用栈
int kthSmallest(TreeNode* root, int k) {
    if(!root) return 0;
    stack<TreeNode *> st;
    while (k) {
        while(root) {
            st.push(root);
            root = root->left;
        }
        k--;
        if (0 == k) return st.top()->val;
        root = st.top()->right; st.pop();
    }
    return st.top()->val;
} 

285.二叉搜索树种的顺序后继

给你一个二叉搜索树和其中的某一个结点,请你找出该结点在树中顺序后继的节点。
结点 p 的后继是值比 p.val 大的结点中键值最小的结点。
LeetCode-二叉树篇_第23张图片
思路
中序遍历: 当遇到p节点的下一个便是答案
迭代寻找: 先判断是否有右儿子,有则寻找右儿子的最左节点, 没有就利用栈和二分查找,添加到达p的节点到栈中,然后通过取出p的父节点,然后判断是否是父节点的左儿子即可

//中序遍历
 bool flag = false;
 TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
     if(!root) return NULL;
     auto t = inorderSuccessor(root->left, p);
     if (t) return t;
     if (flag) {
         return root;
     }
     //找到后设置flag标志位, 那么下一个就是结果了
     if (root == p) {
         flag = true;
     }
     auto t2 = inorderSuccessor(root->right, p);
     if (t2) return t2;
     return NULL;
 }
   
//
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
    if(NULL == root || NULL == p) return NULL;
    TreeNode* node = p->right;
    while(node && node->left)
        node = node->left;
    if(node) return node;
    
    node = root;
    stack<TreeNode*> s;
    //二分查找p节点
    while(node != p){
    	//添加进去都是p的父节点
        s.push(node);
        if(node->val > p->val)
            node = node->left;
        else
            node = node->right;
    }
    
    TreeNode* child  = p;
    TreeNode* parent = NULL;
    while(!s.empty()){
        parent = s.top();
        s.pop();
        //只有左儿子才有可能parent是下一个
        if(parent->left == child)
            return parent;
        child = parent;
    }
    return NULL;
}

298.二叉树最长连续序列

给你一棵指定的二叉树,请你计算它最长连续序列路径的长度。
该路径,可以是从某个初始结点到树中任意结点,通过「父 - 子」关系连接而产生的任意路径。
这个最长连续的路径,必须从父结点到子结点,反过来是不可以的。

LeetCode-二叉树篇_第24张图片

思路
一个全局maxLen, 当parent + 1 == root的时候 len就+1, 然后左右递归

int maxLen = 0;
void help(TreeNode *root, TreeNode * parent,int len)  {
    if (!root) return ;
    if (parent && (parent->val + 1) == root->val) {
    	len++;
	} else {
		len = 1;
	}
    maxLen = max(len, maxLen);
    parent = root;
    help(root->left, parent, len);
    help(root->right, parent, len);
}
int longestConsecutive(TreeNode* root) {
    help(root, NULL, 1);
    return maxLen;
}

333.最大BST子树

给定一个二叉树,找到其中最大的二叉搜索树(BST)子树,其中最大指的是子树节点数最多的。
注意:
子树必须包含其所有后代。
LeetCode-二叉树篇_第25张图片

思路
判断是否是二叉搜索树, 是就计算节点个数, 不是就左右递归寻找

  int largestBSTSubtree(TreeNode* root) {
       if (!root) return 0;
       if (isValid(root, INT_MIN, INT_MAX)) {
           return count(root);
       }
       //以左右儿子重新开始
       return max(largestBSTSubtree(root->left), largestBSTSubtree(root->right));
    }
    //判断是否是二叉寻找树
    bool isValid(TreeNode* root, int min, int max){
        if(!root) return true;
        //如果root节点小于最小, root节点大于最大 返回false
        if (root->val <= min || root->val >= max) return false;
        return isValid(root->left, min, root->val) && isValid(root->right, root->val, max);
    }
    //计算以该root的子树个数
    int count(TreeNode* root){
        if (!root) return 0;
        return 1 + count(root->left) + count(root->right);
    }

366.寻找二叉树的叶子结点

给你一棵二叉树,请按以下要求的顺序收集它的全部节点:
依次从左到右,每次收集并删除所有的叶子节点
重复如上过程直到整棵树为空
LeetCode-二叉树篇_第26张图片

思路
第一步 根据root节点寻找叶子节点, 当到达叶子节点后添加到vector中,并设置为null
然后一直循环 直到root为null

// root 为TreeNode * &的原因是因为如果是TreeNode * 传的是形参,无法改变外部指针
    void help (TreeNode * & root, vector<int> &vt) {
        if (!root) return ;
        //到达叶子节点
        if (!root->left && !root->right) {
            vt.push_back(root->val);
            root = NULL;
            return ;
        }
        //递归
        help(root->left, vt);
        help(root->right, vt);
    }
    vector<vector<int>> findLeaves(TreeNode* root) {
        if (!root) return {};
        vector<vector<int>> ans;
        //循环判断root是否为null
        while (root) {
            vector<int> vt;
            help(root,vt);
            ans.push_back(vt);
        }
        return ans;
    }

426.将二叉搜索树转化为排序的双向链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 。
对于双向循环列表,你可以将左右孩子指针作为双向循环链表的前驱和后继指针,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
特别地,我们希望可以 就地 完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中最小元素的指针。

LeetCode-二叉树篇_第27张图片

思路
设置一个全局的first 和 last
然后中序遍历

Node * first = nullptr;
Node * last = nullptr;
void help(Node *root) {
    if (!root) return;
    //中序遍历
    help(root->left);
    //说明此刻是第一个
    if (last == nullptr) {
        first = root;
    } else {
    	//last是上一个
        last->right = root;
        root->left = last;
    }
    last = root;
    help(root->right);
}
Node* treeToDoublyList(Node* root) {
     if (!root) return NULL;
     help(root);
     //形成闭环
     first->left = last;
     last->right = first;
     return first;
}  

449.序列化和反序列化二叉搜索树

string serialize(TreeNode* root) {
    if(root == nullptr) return {};
    string str;
    queue<TreeNode*> queue;     //层序遍历 辅助队列
    queue.push(root);
    while(!queue.empty()){
        int count = queue.size();
        while(count--){
            TreeNode* node = queue.front();
            queue.pop();
            if(node == nullptr)   //#存储空节点
                str += "#";
            else{
                //不一定是只有10以内的数
                str += to_string(node->val) + "!"; //存储节点值 !分割
                queue.push(node->left);
                queue.push(node->right);
            }
        }
    }
    
    return str;
}

TreeNode* deserialize(string data) {
    if(data.empty())    return NULL;
    int i =0, j=0;
    //查找第一个数
    while(j < data.size() && data[++j] != '!') ;    // [i, j) 存储节点
    TreeNode* head = new TreeNode(stoi(data.substr(i, j-i)) );
    
    queue<TreeNode*> queue;
    queue.push(head);
    
    while(i < data.size()){
        TreeNode* node = queue.front();
        queue.pop();
        if(node == NULL)    continue;
        // 左节点 下一个数开始
        i = ++j;
        if(i >= data.size())    break;
        if(data[i] == '#')
            node->left = nullptr;
        else{
        	//如果是数字,那么就找到数的结尾符
            while(j < data.size() && data[++j] != '!');
            node->left = new TreeNode(stoi(data.substr(i, j-i)) );
        }
        queue.push(node->left);
        // 右节点 下一个数开始
        i = ++j;
        if(i >= data.size())    break;
        if(data[i] == '#')
            node->right = nullptr;
        else{
        	//如果是数字,那么就找到数的结尾符
            while(j < data.size() && data[++j] != '!');
            node->right = new TreeNode(stoi(data.substr(i, j-i)) );
        }
        queue.push(node->right);
    }
    return head;
}

450.删除二叉搜索树中的节点

定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
LeetCode-二叉树篇_第28张图片

思路
1:如果是当前节点没有左右儿子,那么就直接删除
2:如果只有1个孩子,那么用孩子来替代root
3:如果有2个孩子,那么以中序遍历的下一个 , 即右孩子的最左孩子与这个root->交换,然后从右子树开始继续删除

TreeNode* deleteNode(TreeNode* root, int key) {
        if(!root)   return NULL;
        if(key < root -> val){ // 找到key所在的位置
            root -> left = deleteNode(root -> left, key);
            return root;
        }
        if(key > root -> val){ // 找到key所在的位置
            root -> right = deleteNode(root -> right, key);
            return root;
        }
        // 当key == root -> val, 即找到目标点时
            //当目标点只有一边子树时
        if(!root -> left){ // 若没有左子树,删除目标点,右子树接上
            TreeNode* temp = root -> right;
            delete(root);
            return temp;
        }
        if(!root -> right){ // 若没有右子树,删除目标点,左子树接上
            TreeNode* temp = root -> left;
            delete(root);
            return temp;
        }
            //当目标点左右都有子树时
        TreeNode* temp = root -> right; // 找到右子树中最小的值,即进入右子树后一直向左遍历
        while(temp -> left) temp = temp -> left;
        swap(root -> val, temp -> val); // 交换值
        root -> right = deleteNode(root -> right, key); // 进入遍历,删除key
        return root;
    }

508.出现次数最多的子树元素和

给你一个二叉树的根结点,请你找出出现次数最多的子树元素和。一个结点的「子树元素和」定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。
你需要返回出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的子树元素和(不限顺序)。
LeetCode-二叉树篇_第29张图片

思路
首先自底向上计算每个子树和出现的次数, 以和为key存入Map中
然后寻找出现最多的

//保存每个子树和的次数到map中
int process(TreeNode* node, unordered_map<int, int>& map)
{
    if (node == nullptr)
    {
        return 0;
    }

    int left_tree_sum = process(node->left, map);
    int right_tree_sum = process(node->right, map);

    int tree_sum = left_tree_sum + right_tree_sum + node->val;
    ++map[tree_sum];

    return tree_sum;
}

vector<int> findFrequentTreeSum(TreeNode* root) {

    unordered_map<int, int> map; //

    process(root, map);

    vector<int> res;

    int max_count = 0;
    for (const auto& p : map)
    {
        //寻找出现次数最多的
        if (p.second > max_count)
        {
            max_count = p.second;
            res.clear();
            res.push_back(p.first);
        }
        else if(p.second == max_count)
        {
            res.push_back(p.first);
        }
    }

    return res;
}

你可能感兴趣的:(LeetCode刷题)