Leetcode_6【二叉树】

目录

理论基础 

前中后遍历

递归遍历 (必须掌握)

 迭代遍历 (基础不好的录友,迭代法可以放过)

 统一迭代   (值得记)【待补充】

层序遍历 

102.二叉树的层序遍历

107.二叉树的层序遍历Ⅱ

 199.二叉树的右视图

637.二叉树的层平均值

429.N叉树的层序遍历

515.在每个树行中找最大值

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

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

104.二叉树的最大深度

111.二叉树的最小深度

 226.翻转二叉树 (前序遍历√后序遍历√)

 101.对称二叉树(非严格的后序遍历√)

二叉树的深度与高度

104.二叉树的最大深度(后√/前o序遍历;迭代:层序遍历)

N叉树的最大深度

111.二叉树的最小深度

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

110.平衡二叉树

257. 二叉树的所有路径 (前序遍历!)  

404.左叶子之和 (后序遍历!需要回顾)

513.找树左下角的值  

112.路径总和  

113. 路径总和ii 

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

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

654.最大二叉树 

617.合并二叉树 

700.二叉搜索树中的搜索 

98.验证二叉搜索树 

530.二叉搜索树的最小绝对差 

501.二叉搜索树中的众数 

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

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

701.二叉搜索树中的插入操作  

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

669. 修剪二叉搜索树 

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

538.把二叉搜索树转换为累加树  

总结篇  


理论基础 

需要了解 二叉树的种类,存储方式,遍历方式 以及二叉树的定义 

文章讲解:代码随想录

前中后遍历

递归遍历 (必须掌握)

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

step2:确定终止条件

step3:确定单层递归的逻辑

二叉树的三种递归遍历掌握其规律后,其实很简单 

题目链接:

文章讲解/视频讲解:代码随想录

前序:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        traversal(root, res);
        return res;
    }
    void traversal(TreeNode* cur, vector<int>& vec){//ATTENTION
        if (cur == NULL) return;
        vec.push_back(cur->val);
        traversal(cur->left, vec);
        traversal(cur->right, vec);
    }
};

中序:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        traversal(root,res);
        return res;     
    }
    void traversal(TreeNode* root, vector<int>& vec){
        if(root == NULL) return;
        traversal(root->left,vec);
        vec.push_back(root->val);
        traversal(root->right,vec);
    }
};

后序:

class Solution {
public:    
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        traversal(root,res);
        return res;
    }
    void traversal(TreeNode* cur, vector<int>& vec){
        if(cur == NULL) return;
        traversal(cur->left,vec);
        traversal(cur->right,vec);
        vec.push_back(cur->val);
    }
};

用visual studio调试

#include <windows.h>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

TreeNode* buildTree(const vector<int>& vals) {
	if (vals.empty()) return nullptr;

	TreeNode *root = new TreeNode(vals[0]);
	queue<TreeNode*> q;
	q.push(root);
	size_t i = 1;

	while (!q.empty() && i < vals.size()) {
		TreeNode *node = q.front();
		q.pop();

		if (vals[i] != NULL) {
			node->left = new TreeNode(vals[i]);
			q.push(node->left);
		}
		++i;

		if (i < vals.size() && vals[i] != NULL) {
			node->right = new TreeNode(vals[i]);
			q.push(node->right);
		}
		++i;
	}
	return root;
}

class Solution {
public:
	vector<int> preorderTraversal(TreeNode* root) {
		vector<int> res;
		traversal(root, res);
		return res;
	}
	void traversal(TreeNode* node, vector<int>& res) {
		if (node == NULL) return;
		res.push_back(node->val);
		traversal(node->left, res);
		traversal(node->right, res);
	}
};

int main(){
	//定义二叉树--方法1
	//vector<int> values = { 3, 2, 3, NULL, 3, NULL, 1 };
	//TreeNode *root = buildTree(values);
	//定义二叉树--方法2
	TreeNode* root = new TreeNode(3);
	root->left = new TreeNode(2);
	root->right = new TreeNode(3);
	root->left = new TreeNode(2);
	root->right = new TreeNode(2);
	Solution ceshi;
	vector<int> result = ceshi.preorderTraversal(root);
	std::cout << result[0] << result[1] << result[2] << result[3] << result[4] << std::endl;
}

 迭代遍历

题目链接(和上面一样,但是用迭代做):

文章讲解/视频讲解:代码随想录

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。因此用栈也可以是实现二叉树的前后中序遍历了。

前序遍历

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                       // 中
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);           // 右(空节点不入栈)
            if (node->left) st.push(node->left);             // 左(空节点不入栈)
        }
        return result;
    }
};

中序遍历

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

后序遍历:中右左--->左右中

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};

 统一迭代   (值得记)【待补充】

题目链接(和上面一样,但是用迭代做)

文章讲解:代码随想录

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop(); 
                if (node->right) st.push(node->right); 
                st.push(node); //*                 
                st.push(NULL); //*

                if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈)
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.top();    // 重新取出栈中元素
                st.pop();
                result.push_back(node->val); // 加入到结果集
            }
        }
        return result;
    }
};

114. 二叉树展开为链表

在前序遍历的基础上,更改root的结构

class Solution {
public:
    void flatten(TreeNode* root) {
        if(root == nullptr) return;
        vector<TreeNode*> vec;
        iter(root, vec);
        for(int i = 1; i < vec.size(); i++){
            TreeNode* pre = vec[i - 1];
            TreeNode* cur = vec[i];
            pre->left = nullptr;
            pre->right = cur;
        }
        return;
    }
    void iter(TreeNode* node, vector<TreeNode*> &vec){//注意加这个&
        if(node == nullptr) return;
        vec.push_back(node);
        if(node->left) iter(node->left, vec);
        if(node->right) iter(node->right, vec);
    }
};

层序遍历 

题目链接:

文章讲解/视频讲解:代码随想录

102.二叉树的层序遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        queue<TreeNode*> que;//ATTENTION:*
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();//ATTENTION
            vector<int> vec; //ATTENTION:要在这里定义,不然的话vec会累加上一次的就不是单次的了
            while(size--){//ATTENTION:每pop一个节点就push这个节点的左右孩子
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);//ATTENTION:->val
                if(node->left != NULL) que.push(node->left);
                if(node->right != NULL) que.push(node->right);
            }
            res.push_back(vec);
        }
        return res;
    }
};

107.二叉树的层序遍历Ⅱ

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> res;
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){//ATTENTION: NOT: que != NULL
            vector<int> vec;
            int size = que.size(); 
            while(size--){
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if(node->left!=NULL) que.push(node->left);
                if(node->right!=NULL) que.push(node->right);
            }
            res.push_back(vec);
        }
        reverse(res.begin(),res.end());//ATTENTION
        return res;
    }
};

 199.二叉树的右视图

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {             
        vector<int> res;
        queue<TreeNode*> que;
        // step1: 导入根节点
        if(root!=NULL) que.push(root);
        while(!que.empty()){
            int rightSide;
            int size = que.size();
            while(size--){
                TreeNode* node = que.front();
                if(size == 0) rightSide = node->val;//size = 0就是最后一个数咯
                que.pop();
                if(node->left != NULL) que.push(node->left);
                if(node->right != NULL) que.push(node->right);
            }
            res.push_back(rightSide);
        }
        return res;
    }
};

637.二叉树的层平均值

class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        vector<double> res;
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            int size_backup = size;//ATTENTION:size_backup 
            double sum = 0;//ATTENTION:注意数据类型是double
            while(size--){
                TreeNode* node = que.front();
                sum += node->val;
                que.pop();
                if(node->left != NULL) que.push(node->left);
                if(node->right != NULL) que.push(node->right);
            }
            res.push_back(sum/size_backup);
        }
        return res;
    }
};

429.N叉树的层序遍历

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> res;
        queue<Node*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            vector<int> cur;
            int size = que.size();
            while(size--){
                Node* node = que.front();
                cur.push_back(node->val);
                que.pop();
                for(int i = 0; i < node->children.size(); i++){//ATTENTION: i < node->children.size()
                    que.push(node->children[i]);
                }
            }
            res.push_back(cur);
        }
        return res;
    }
};


515.在每个树行中找最大值

class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int> res;
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            int max = INT32_MIN;
            while(size--){
                TreeNode* node = que.front();
                max = max < node->val?node->val:max;
                que.pop();
                if(node->left != NULL) que.push(node->left);
                if(node->right != NULL) que.push(node->right);
            }
            res.push_back(max);
        }
        return res;
    }
};


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

层序遍历,二刷,我的

完美二叉树

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            while(size--){
                Node* node = que.front();
                que.pop();
                if(size != 0){
                    Node* node2 = que.front();
                    node->next =node2;
                }else node->next = NULL;
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return root;
    }
};

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

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if(root != nullptr) que.push(root);
        else return root;
        while(!que.empty()){
            int size = que.size();
            Node* pre = nullptr;
            while(size--){
                Node* node = que.front();
                if(size == 0) node->next = nullptr;
                if(pre) pre->next = node;
                pre = node;
                que.pop();
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return root;        
    }
};
 


104.二叉树的最大深度

(下面有)


111.二叉树的最小深度

(下面有)

103. 二叉树的锯齿形层序遍历

class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> res;
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        int i = 0;
        while(!que.empty()){
            int size = que.size();
            vector<int> vec;
            while(size--){
                TreeNode* node = que.front();
                vec.push_back(node->val);//或者这里用双端队列deque接,分情况push_back or push_front;
                que.pop();
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            if(i % 2 == 0) res.push_back(vec);
            else{
                reverse(vec.begin(), vec.end());
                res.push_back(vec);
            }
            i++;
        }
        return res;
    }
};

 226.翻转二叉树 (前序遍历√后序遍历√)

这道题目一些做过的同学理解的也不够深入,先看视频讲解,无论做过没做过,都会有很大收获。

题目链接:226. 翻转二叉树 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

关键是swap函数记得用啊~

下面程序段都行,可以放心root都是变换后的根节点,无论前序还是后序遍历。

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        traversal_reverse(root);
        return root;

    }
    void traversal_reverse(TreeNode* node){
        if(node ==NULL) return;
        traversal_reverse(node->left);
        traversal_reverse(node->right);
        swap(node->left,node->right);//前序或者后序都可以,但是不可以中序
    }
};
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        // TreeNode* res;
        return traveral(root);
    }
    TreeNode* traveral(TreeNode* node){
        //以后序遍历为例
        if(node == NULL) return node;
        traveral(node->left);
        traveral(node->right);
        swap(node->left, node->right);//后序遍历
        return node;
    }
};
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 左
        invertTree(root->right);        // 右
        return root;
    }
};

 101.对称二叉树(非严格的后序遍历√)

先视频讲解,关键是左右孩子相互翻转之后是否可以重叠,且节点的数值相同

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解/视频讲解:代码随​​​​​​想录

左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        bool res = compare(root->left,root->right);
        return res;
    }
    bool compare(TreeNode* left, TreeNode* right){
        if(left == NULL && right != NULL) return false;
        else if(left != NULL && right == NULL) return false;
        else if(left == NULL && right == NULL) return true;
        else if(left->val != right->val) return false;

        bool outside = compare(left->left,right->right);
        bool inside  = compare(left->right,right->left);
        return outside && inside;
    }
};

和下面相同的树相比较,对称二叉树要考虑将迭代函数设置两个参数,迭代的时候还要考虑外侧和外侧比,内侧和内侧比。是个难点。

100. 相同的树

100. 相同的树

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(p == nullptr && q == nullptr) return true;
        else if(p != nullptr && q == nullptr) return false;
        else if(p == nullptr && q != nullptr) return false;
        else if(p->val == q->val) return(isSameTree(p->left, q->left) && isSameTree(p->right,q->right));
        else return false;
    }
};

二叉树的深度与高度

对于某一个节点而言,前序求的就是节点的深度,使用后序求的是节点的高度。

对于二叉树而言,根节点的高度就是二叉树的最大深度。因此用后序求二叉树最大深度比较简单。而对于最小深度,有差异不可直接1+min。

104.二叉树的最大深度(后√/前o序遍历;迭代:层序遍历)

什么是深度,什么是高度,如何求深度,如何求高度,这里有关系到二叉树的遍历方式。

大家 要先看视频讲解,就知道以上我说的内容了,很多录友刷过这道题,但理解的还不够。

题目链接:104. 二叉树的最大深度 - 力扣(LeetCode)

文章讲解/视频讲解: 代码随想录

【迭代:层序遍历】

class Solution {
public:
    int maxDepth(TreeNode* root) {
    queue<TreeNode*> que;
    int depth = 0;
    if(root!=NULL) que.push(root);
    while(!que.empty()){
        int size = que.size();
        while(size--){
            TreeNode* node = que.front();
            que.pop();
            if(node->left!=NULL) que.push(node->left);
            if(node->right!=NULL) que.push(node->right);
        }
        depth++;
    }
    return depth;
    }
};

【递归:后序遍历】---判断平衡二叉树在此基础上修改

class Solution {
public:
    int maxDepth(TreeNode* root) {
        return getDepth(root);

    }
    int getDepth(TreeNode* node){
        if(node == NULL) return 0;

        int leftDepth = getDepth(node->left);
        int rightDepth = getDepth(node->right);

        return 1 + max(leftDepth,rightDepth);

    }
};

求最大深度(需要回顾)

class Solution {
public:
    int result;
    void getDepth(TreeNode* node, int depth) {
        result = depth > result ? depth : result; // 中
        if (node->left == NULL && node->right == NULL) return ;
        if (node->left) { // 左
            getDepth(node->left, depth + 1);
        }
        if (node->right) { // 右
            getDepth(node->right, depth + 1);
        }
        return ;
    }
    int maxDepth(TreeNode* root) {
        result = 0;
        if (root == 0) return result;
        getDepth(root, 1);
        return result;
    }
};

N叉树的最大深度

题目链接:559. N 叉树的最大深度 - 力扣(LeetCode)

代码:

【层序遍历】

class Solution {
public:
    int maxDepth(Node* root) {
        queue<Node*> que;
        if(root != NULL) que.push(root);
        int depth = 0;
        while(!que.empty()){
            int size = que.size();
            depth++;
            for (int i = 0; i < size; i++) {
                Node* node = que.front();
                que.pop();
                for(int j = 0; j < node->children.size() ; j++){//ATTENTION
                    if(node->children[j]) que.push(node->children[j]);
                }        
            }
        } 
        return depth;
    }
};

【后序遍历】

class Solution {
public:
    int maxDepth(Node* root) {
        return getDepth(root);

    }
    int getDepth(Node* node){
        if(node == NULL) return 0;
        int maxCur = 0;
        for(int i = 0; i < node->children.size(); i++){
            maxCur = max(maxCur, getDepth(node->children[i]));//有函数可以用
            // max = max > getDepth(node->children[i]) ? max : getDepth(node->children[i]);
        }
        return 1 + maxCur;
    }
};

111.二叉树的最小深度

先看视频讲解,和最大深度 看似差不多,其实 差距还挺大,有坑。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点。

什么是叶子节点,左右孩子都为空的节点才是叶子节点!

  • 对于有左有右的节点,1+min(right,left)
  • 对于只有一边儿的节点,1+这边的最小深度。(这时不能1+min(right,left),因为right或者left一定有个是0)
  • 对于无左无右的节点,深度就是1

题目链接:111. 二叉树的最小深度 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

class Solution {
public:
    int minDepth(TreeNode* root) {
        return getHeight(root);
    }
    int getHeight(TreeNode* root){
        if(root==NULL) return 0;
        int left_height = getHeight(root->left);
        int right_height = getHeight(root->right);
        //当左节点是空,右节点不是空时:不是叶子节点
        if(root->left == NULL && root->right != NULL) return 1+right_height;
        //当右节点时空,左节点不是空时:不是叶子节点
        if(root->right == NULL && root->left != NULL) return 1+left_height;
        return 1+min(right_height, left_height);
    }
};

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

需要了解,普通二叉树 怎么求,完全二叉树又怎么求

对于完全二叉树:如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量

题目链接:代码随想录 (programmercarl.com)

文章讲解/视频讲解:代码随想录

如果是普通二叉树

迭代

class Solution {
public:
    int countNodes(TreeNode* root) {
        //如果是空节点,返回
        if(root == NULL) return 0;
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

递归

class Solution {
public:
    int countNodes(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        int result = 0;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                result++;   // 记录节点数量
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

对于完全二叉树,可以先判断是不是子节点是不是满二叉树,提前结束递归

  • 在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1)  个节点。

  • 完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。

    对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。

    对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。可以看出如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量。

  • 如何去判断一个左子树或者右子树是不是满二叉树呢?

    在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树

class Solution {
public:
    int countNodes(TreeNode* root) {
        //如果是空节点,返回
        if(root == NULL) return 0;
        //如果遇到满二叉树,可以用公式求,就可以提前返回了
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        int leftDepth = 0, rightDepth = 0;
        while(left){
            left = left->left;
            leftDepth++;
        }
        while(right){
            right = right->right;
            rightDepth++;
        }
        if(leftDepth == rightDepth){
            return (2<<leftDepth)- 1; //2<<1相当于2^2
        }
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

110.平衡二叉树

再一次涉及到,什么是高度,什么是深度,可以巩固一下。

一棵高度平衡二叉树定义为:二叉树每个节点 的左右两个子树的高度差的绝对值不超过1

这个题的难点在于,需要在返回数据中同时存入两个信息:是否是平滑二叉树,以及,此节点二叉树的高度,因此用-1表示不是平衡二叉树,其他用来表示二叉树的高度。

题目链接:110. 平衡二叉树 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

class Solution {
public:
    // 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
    int getHeight(TreeNode* node) {
        if (node == NULL) {
            return 0;
        }
        int leftHeight = getHeight(node->left);
        if (leftHeight == -1) return -1;
        int rightHeight = getHeight(node->right);
        if (rightHeight == -1) return -1;
        return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
    }
    bool isBalanced(TreeNode* root) {
        return getHeight(root) == -1 ? false : true;
    }
};

第二次写

class Solution {
public:
    bool isBalanced(TreeNode* root) {
        if(iter(root) == -1) return false;
        else return true;     
    }
    int iter(TreeNode* node){// -1 no; x height;
        //对空节点
        if(node == nullptr) return 0;
        //对非空节点
        else{
            int leftHeight = iter(node->left);
            int rightHeight = iter(node->right);
            if(leftHeight == -1 || rightHeight == -1) return -1;//只要有一个节点不满足,就统统不满足
            else if(abs(leftHeight - rightHeight) <= 1) return max(leftHeight, rightHeight) + 1; //只有当高度差小于等于1才计算本节点高度
            else return -1;   //否则判断本节点不满足
        }
    }
};

257. 二叉树的所有路径 (前序遍历!回溯登场!)  

题目链接:257. 二叉树的所有路径 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

需要用到前序遍历,因为需要指向它的左孩子和右孩子

递归法:

原创:

class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        res.clear();
        if(root == NULL) return res;
        pathInt.push_back(root->val);
        traversal(root);
        return res;
    }
private:
    vector<string> res;
    vector<int> pathInt;
    string pathStr;
    void traversal(TreeNode* node){
        //if(node == NULL) return;
        if(node->left == NULL && node->right == NULL){
            pathStr = standard(pathInt);
            res.push_back(pathStr);//path中包含本层节点
            return;
        }

        if(node->left){
            pathInt.push_back(node->left->val);
            traversal(node->left);
            pathInt.pop_back();
        }
        if(node->right){
            pathInt.push_back(node->right->val);
            traversal(node->right);
            pathInt.pop_back();
        }
        return ;
    }
    string standard(vector<int> vec){
        string str = "" ;
        for(int i = 0; i < vec.size(); i++){
            str += to_string(vec[i]);//to_string
            if(i != vec.size() - 1)  str += "->";
        }
        return str;
    }

上下两个代码的区别是

1、path和res可以作为全局变量用,以保存值,也可以以指针的形式传到递归函数里面

2、上面的代码里path在进入本层之前已经包含本层节点;下面的代码里在本层将本层节点放在path里的

class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        vector<int> path;
        if(root == NULL) return res;
        Iter(root,path,res);
        return res;
    }
private:
    //Pre-order traversal
    void Iter(TreeNode* node, vector<int>& path, vector<string>& res){
        //middle
        string res_cell;
        path.push_back(node->val);
        if(node->left == NULL && node->right == NULL){
            res_cell = Standard(path);
            res.push_back(res_cell);
            return;
        }
        //front
        if(node->left != NULL){
            Iter(node->left, path, res);
            path.pop_back();//ATTENTION:not pop
        }
        //back
        if(node->right != NULL){
            Iter(node->right, path, res);
            path.pop_back();
        }
    }
    string Standard(vector<int> digit){
        int size = digit.size();
        string res_cell;
        for(int i = 0; i < size-1; i++){
            res_cell += to_string(digit[i]);
            res_cell += "->";
        }
        res_cell += to_string(digit[size-1]);
        return res_cell;
    }
};

404.左叶子之和 (后序遍历!需要回顾)

其实本题有点文字游戏,搞清楚什么是左叶子,剩下的就是二叉树的基本操作。 

题目链接:404. 左叶子之和 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历

递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if(root == NULL) return 0;
        if(root->left == NULL && root->right == NULL) return 0;

        //后序遍历
        int leftValue = sumOfLeftLeaves(root->left);
        if(root->left != NULL && root->left->left == NULL && root->left->right == NULL){
            leftValue = root->left->val;
        }
        int rightValue = sumOfLeftLeaves(root->right);
        int sum = leftValue + rightValue;
        return sum;
    }
};

513.找树左下角的值  

本地递归偏难,反而迭代简单属于模板题, 两种方法掌握一下 

题目链接:文章讲解/视频讲解:代码随想录

递归:

迭代:

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        int res;
        while(!que.empty()){
            int size = que.size();
            for(int i = 0; i < size; i++){
                TreeNode* node = que.front();
                if(i == 0) res = node->val;
                que.pop();
                if(node->left != NULL) que.push(node->left);
                if(node->right != NULL) que.push(node->right);
            }
        }
        return res;
    }
};

112.路径总和  

本题 又一次设计要回溯的过程,而且回溯的过程隐藏的还挺深,建议先看视频来理解 

112. 路径总和,和 113. 路径总和ii 一起做了。 优先掌握递归法。

没有中节点的处理逻辑,前中后序遍历都可以

没有必要遍历所有节点,如果找到一个条路径就可以立刻返回了

题目链接:112. 路径总和 - 力扣(LeetCode)

文章讲解/视频讲解:代码随想录

下面这个count是剪完本层之后的数值

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == NULL) return false;
        return traveral(root, targetSum - root->val);//牢记这里的count是剪完本层之后的数值
    }

    bool traveral(TreeNode* node, int count){//count是剪完本层node之后的数据
        if(node->left == NULL && node->right == NULL && count == 0) return true;
        if(node->left == NULL && node->right == NULL && count != 0) return false;
        if(node->left != NULL){
            count -= node->left->val;
            if(traveral(node->left, count) == true) return true;//一直到root之后就结束了
            count += node->left->val;

            //可以用一句:if(node->left != nullptr) if(iter(node->left, target - node->left->val)) return true;
        }
        if(node->right != NULL){
            count -= node->right->val;
            if(traveral(node->right, count) == true) return true;//一直到root之后就结束了
            count += node->right->val;
        }
        return false;
    }
};

下面这个count是还没剪完本层之后的数值

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == NULL) return false;
        if(root->left == NULL && root->right == NULL && targetSum == root->val) return true;
        return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
    }
};

但是下面这样不行,因为left或者right可能没被定义

errrrroooooorrrrrr//
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == nullptr) return false;
        return iter(root, targetSum - root->val);

    }

    bool iter(TreeNode* node, int target){
        if(node->left == nullptr && node->right == nullptr){
            if(target == 0) return true;
            else return false;
        } 
        if(node->left != nullptr) bool left = iter(node->left, target - node->left->val) ;
        if(node->right!= nullptr) bool right = iter(node->right, target - node->right->val) ;
        return left || right;
    }
};

113. 路径总和ii 

题目链接:113. 路径总和 II - 力扣(LeetCode)

这里需要保存所有符合条件的路径,因此递归函数没有返回值,且需要有变量存路径值

public:
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        result.clear();
        path.clear();
        if (root == NULL) return result;
        path.push_back(root->val);//先存一下根节点
        traversal(root, targetSum - root->val);//count是减去当前之后的值
        return result;
    }
private:
    vector<vector<int>> result;
    vector<int> path;

    void traversal(TreeNode* node, int count){
        if(node->left == NULL && node->right ==NULL && count == 0) {
            result.push_back(path);//count == 0存一下再返回
            return ;
        }
        if(node->left == NULL && node->right ==NULL) return ;//count != 0直接返回

        if(node->left){
            path.push_back(node->left->val);
            count -= node->left->val;
            traversal(node->left, count);
            count += node->left->val;
            path.pop_back();//ATTENTION:没有参数
        }

        if(node->right){
            path.push_back(node->right->val);
            count -= node->right->val;
            traversal(node->right, count);
            count += node->right->val;
            path.pop_back();
        }

        return ;
    }

构造二叉树

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

本题算是比较难的二叉树题目了,大家先看视频来理解。 

106.从中序与后序遍历序列构造二叉树,105.从前序与中序遍历序列构造二叉树 一起做,思路一样的

从后序遍历中判断中间节点

从中序遍历中分割左右节点

  • 第一步:如果数组大小为零的话,说明是空节点了。

  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。

  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点

  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

  • 第五步:切割后序数组,切成后序左数组和后序右数组

  • 第六步:递归处理左区间和右区间

题目链接/文章讲解/视频讲解:代码随想录

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(inorder.size() == 0 || postorder.size() == 0) return nullptr;
        return iter(inorder,0,inorder.size(),postorder,0,postorder.size());

    }

    TreeNode* iter(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postBegin, int postEnd){
        //返回条件
        if(inorderEnd == inorderBegin) return nullptr;
        //新建一个节点,值是--中
        int nodevalue = postorder[postEnd - 1];
        TreeNode* node = new TreeNode(nodevalue);
        //叶子节点返回
        if(postEnd - postBegin == 1) return node;//左闭右开,说明只有一个叶子节点了
        //中在中序中的位置
        int index;
        for(int i = inorderBegin; i < inorderEnd; i++){
            if(inorder[i] == nodevalue){
                index = i;//中在中序中的位置
                break;
            }
        }
        //分割中序
        int leftinorderBegin = inorderBegin;
        int leftinorderEnd = index;
        int rightinorderBegin = index + 1;
        int rightinorderEnd = inorderEnd;
        //分割后序
        int leftpostorderBegin = postBegin;
        int leftpostorderEnd = postBegin + (leftinorderEnd - leftinorderBegin);
        int rightpostorderBegin = postBegin + (leftinorderEnd - leftinorderBegin);
        int rightpostorderEnd = postEnd - 1;//排除最后一个元素

        //递归
        node->left = iter(inorder, leftinorderBegin,leftinorderEnd,postorder,leftpostorderBegin,leftpostorderEnd);
        node->right = iter(inorder, rightinorderBegin, rightinorderEnd, postorder, rightpostorderBegin, rightpostorderEnd);
        
        //如果进行到这里了,那返回的就是根节点了
        return node;
    }
};

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

文章: 代码随想录

654.最大二叉树 

又是构造二叉树,昨天大家刚刚做完 中序后序确定二叉树,今天做这个 应该会容易一些, 先看视频,好好体会一下 为什么构造二叉树都是 前序遍历 

题目链接:654. 最大二叉树

文章讲解:代码随想录

视频讲解:又是构造二叉树,又有很多坑!| LeetCode:654.最大二叉树_哔哩哔哩_bilibili

class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        int index, maxVaule = INT_MIN;      
        if(nums.size() == 0) return nullptr;
        for(int i = 0; i < nums.size(); i++){
            if(nums[i] > maxVaule){
                index = i;
                maxVaule = nums[i];
            }
        }
        TreeNode* node = new TreeNode(maxVaule);//中
        //叶子节点
        if(nums.size() == 1) return node;
        if(index > 0){//存在左子树
            vector<int> left(nums.begin(), nums.begin() + index);//因为左闭右开,得保证index > 0
            node->left = constructMaximumBinaryTree(left);
        }
        if(index < nums.size() - 1){//存在右子树
            vector<int> right(nums.begin() + index + 1, nums.end());
            node->right = constructMaximumBinaryTree(right);
        }
        return node;
    }
};

617.合并二叉树 

这次是一起操作两个二叉树了, 估计大家也没一起操作过两个二叉树,也不知道该如何一起操作,可以看视频先理解一下。 优先掌握递归。

题目链接/文章讲解:代码随想录

视频讲解:一起操作两个二叉树?有点懵!| LeetCode:617.合并二叉树_哔哩哔哩_bilibili

这种做法没开辟新空间:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if(root1 == nullptr && root2 == nullptr) return nullptr;
        else if(root1 != nullptr && root2 == nullptr) return root1;
        else if(root1 == nullptr && root2 != nullptr) return root2;
        else{
            root1->val += root2->val;//将root2加到root1上了
        }
        root1->left = mergeTrees(root1->left, root2->left);
        root1->right = mergeTrees(root1->right, root2->right);
        // 运行到这就是根节点了
        return root1;
    }
};

二叉搜索树

700.二叉搜索树中的搜索 

递归和迭代 都可以掌握以下,因为本题比较简单, 了解一下 二叉搜索树的特性

题目链接:. - 力扣(LeetCode)

文章讲解: 代码随想录

视频讲解:不愧是搜索树,这次搜索有方向了!| LeetCode:700.二叉搜索树中的搜索_哔哩哔哩_bilibili

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if(root == nullptr) return nullptr;
        if(root->val == val) return root;
        if(root->val > val) return searchBST(root->left, val);
        if(root->val < val) return searchBST(root->right, val);
        return nullptr;
    }
};

如果没用二叉搜索树的特性的话,一些案例会超时

///timeout
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if(root == nullptr) return nullptr;
        else if(root != nullptr && root->val == val) return root;
        else 
            if(searchBST(root->left, val)!= nullptr) return searchBST(root->left, val);
            else if(searchBST(root->right, val)!= nullptr) return searchBST(root->right, val);
            else return nullptr;
    }
};

所以还是得检查node->val与val的大小,来判断搜索方向

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if(root == nullptr) return nullptr;
        else if(root->val == val) return root;
        else if(root->val > val)  return searchBST(root->left, val);
        else return searchBST(root->right, val);
    }
};

98.验证二叉搜索树 

遇到 搜索树,一定想着中序遍历,这样才能利用上特性。 

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

视频讲解:你对二叉搜索树了解的还不够! | LeetCode:98.验证二叉搜索树_哔哩哔哩_bilibili

但本题是有陷阱的:左子树小于右子树,而不只是左节点小于右节点。
所以觉得先用中序遍历存起来,然后再判断是不是递增数列,比较直观。
迭代遍历的vec可以使传引用,也可以是全局变量这样用。
class Solution {
public:
    vector<int> vec;
    bool isValidBST(TreeNode* root) {
        // vector<int> vec;
        if(root == nullptr) return true;
        iter(root);
        for(int i = 0; i < vec.size() - 1; i++){
            if(vec[i] >= vec[i+1]) return false;
        }
        return true;
    }
    
    void iter(TreeNode* node){
        if(node == nullptr) return;
        iter(node->left);
        vec.push_back(node->val);
        iter(node->right);
    }
};

530.二叉搜索树的最小绝对差 

需要领悟一下二叉树遍历上双指针操作,优先掌握递归 

题目链接:

文章讲解:代码随想录

视频讲解:二叉搜索树中,需要掌握如何双指针遍历!| LeetCode:530.二叉搜索树的最小绝对差_哔哩哔哩_bilibili

501.二叉搜索树中的众数 

和 530差不多双指针思路,不过 这里涉及到一个很巧妙的代码技巧。

可以先自己做做看,然后看我的视频讲解。

文章讲解:代码随想录

视频讲解:不仅双指针,还有代码技巧可以惊艳到你! | LeetCode:501.二叉搜索树中的众数_哔哩哔哩_bilibili

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

本题其实是比较难的,可以先看我的视频讲解 

文章讲解:代码随想录

视频讲解:自底向上查找,有点难度! | LeetCode:236. 二叉树的最近公共祖先_哔哩哔哩_bilibili

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

对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。 

题目链接:

文章讲解:代码随想录

视频讲解:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili

701.二叉搜索树中的插入操作  

本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。

题目链接:

文章讲解:代码随想录

视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili

450.删除二叉搜索树中的节点 [回顾]

相对于插入操作,本题就有难度了,涉及到改树的结构 

题目链接:450. 删除二叉搜索树中的节点

文章讲解:代码随想录

视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili

这个题的难点在于删除节点之后的拼接,对于找到的节点左右都有节点的二叉树而言,可以认为将左树移植到了右数的左下角

可以和二叉搜索树的搜索,这个题目类比记忆。

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(root == nullptr) return nullptr;
        if(root->val == key){
            if(root->left == nullptr && root->right == nullptr){
                auto reNode = root;
                delete reNode;
                return nullptr;
            }else if(root->left == nullptr){
                auto reNode = root->right;
                delete root;
                return reNode;
            }else if(root->right == nullptr){
                auto reNode = root->left;
                delete root;
                return reNode;
            }else{
                TreeNode* cur = root->right;
                while(cur->left != nullptr){
                    cur = cur->left;
                }
                cur->left = root->left;
                TreeNode* temp = root;
                root = root->right;
                delete temp;
                return root;
            }
        }
        if(root->val > key) root->left =  deleteNode(root->left, key);
        if(root->val < key) root->right = deleteNode(root->right, key);
        return root;
    }
};

669. 修剪二叉搜索树 

这道题目比较难,比 添加增加和删除节点难的多,建议先看视频理解。

题目链接:

文章讲解: 代码随想录

视频讲解: 你修剪的方式不对,我来给你纠正一下!| LeetCode:669. 修剪二叉搜索树_哔哩哔哩_bilibili

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

本题就简单一些,可以尝试先自己做做。

题目连接:108. 将有序数组转换为二叉搜索树

文章讲解:代码随想录

视频讲解:构造平衡二叉搜索树!| LeetCode:108.将有序数组转换为二叉搜索树_哔哩哔哩_bilibili

538.把二叉搜索树转换为累加树  

本题不难,在 求二叉搜索树的最小绝对差 和 众数 那两道题目 都讲过了 双指针法,思路是一样的。

文章讲解:代码随想录

视频讲解:普大喜奔!二叉树章节已全部更完啦!| LeetCode:538.把二叉搜索树转换为累加树_哔哩哔哩_bilibili

总结篇  

代码随想录

100. 相同的树

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值