More content:力扣题记之二叉树
链式存储的二叉树节点定义方式:
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
- 二叉树递归求深度用前序,求高度用后序
 - C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,而unordered_map、unordered_set,底层实现是哈希表
 - 257.二叉树的所有路径 + 112. 路径总和 = 113. 路径总和ii(跟着走一遍,彻底搞懂递归和回溯)
 
94. 二叉树的中序遍历✅
递归
class Solution {
public:
    void traversal(TreeNode *node, vector<int> &v) {
        if(!node) return ;
        traversal(node->left, v);
        v.push_back(node->val);
        traversal(node->right, v);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> v;
        traversal(root, v);
        
        return v;
    }
};
104. 二叉树的最大深度✅
Solution1:递归遍历,使用前序求的是深度,使用后序求的是高度
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
 - 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
 
本题求最大深度,根节点的高度就是二叉树的最大深度,因此既可以使用前序遍历 ,也可以使用后序遍历
Solution2:层序遍历
class Solution1 {
public:
    int traversal(TreeNode *cur) {
        if(!cur) return 0;
        int l = traversal(cur->left);
        int r = traversal(cur->right);
        return max(l, r) + 1;
    }
    int maxDepth(TreeNode* root) {
        return traversal(root);
    }
};
class Solution2 {
public:
    int maxDepth(TreeNode* root) {
        deque<TreeNode*> dq;
        int res = 0;
        if(root) dq.push_back(root);
        while(!dq.empty()) {
            int size = dq.size();
            res++;
            while(size--) {
                TreeNode* cur = dq.front();
                dq.pop_front();
                if(cur->left) dq.push_back(cur->left);
                if(cur->right) dq.push_back(cur->right);
            }
        }
        return res;
    }
};
226. 翻转二叉树✅
递归三步走
简单算法题不用递归没有意义 锻炼递归思维
class Solution {
public:
    void myReverse(TreeNode* cur) {
        if(!cur) return ;
        TreeNode *tmp = cur->left;
        cur->left = cur->right;
        cur->right = tmp;
        myReverse(cur->left);
        myReverse(cur->right);
    }
    TreeNode* invertTree(TreeNode* root) {
        myReverse(root);
        return root;
    }
};
101. 对称二叉树❌
队列模拟太麻烦了 建议递归
注意判断逻辑怎么样最简(比代码随想录简单一倍)
class Solution {
public:
    bool digui(TreeNode* left, TreeNode* right) {
        //左右节点都为空 对称
        if(!left && !right) return true;
        //左右节点只有一个为空 或者val不相同 不对称
        if(!left || !right || left->val != right->val) return false;
        bool f = digui(left->left, right->right) && digui(right->left, left->right);
        return f;
    }
    bool isSymmetric(TreeNode* root) {
        return digui(root->left, root->right);
    }
};
543. 二叉树的直径❌
后序遍历就是天然的回溯
class Solution {
private:
    int ans;
public:
    int travseral(TreeNode *cur) {
        if(!cur) return 0;
   
        int l = travseral(cur->left);
        int r = travseral(cur->right);
        ans = max(ans, l + r + 1); //维护最长直径
        
        return max(l, r) + 1; //返回最大单侧路径长度,往上传递
    }
    int diameterOfBinaryTree(TreeNode* root) {
        ans = 1;
        travseral(root);
     
        return ans - 1;
    }
};
102. 二叉树的层序遍历✅
STL队列
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        deque<TreeNode*> dq;
        vector<vector<int>> res;
        if(!root) return res;
        
        dq.push_back(root);
        while(!dq.empty()) {
            int size = dq.size();
            vector<int> v;
            while(size--) {
                TreeNode *cur = dq.front();
                v.push_back(cur->val);
                dq.pop_front();
                if(cur->left) dq.push_back(cur->left);
                if(cur->right) dq.push_back(cur->right);
            }
            res.push_back(v);
        }
        return res;
    }
};
108. 将有序数组转换为二叉搜索树❌
递归取数组中间值
class Solution {
public:
    TreeNode* travseral(vector<int> &nums, int left, int right) {
        //贯彻左闭右闭
        if(left > right) return nullptr;
        int mid = left + (right - left) / 2; //防止当left和right都是int最大值时溢出
        TreeNode *cur = new TreeNode(nums[mid]);
        cur->left = travseral(nums, left, mid-1);
        cur->right = travseral(nums, mid+1, right);
        return cur;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        TreeNode *root = travseral(nums, 0, nums.size() - 1);
        
        return root;
    }
};
98. 验证二叉搜索树❌✅
Solution1:中序遍历,验证遍历的元素是不是从小到大
Solution2:中序遍历取出BST的val,看是否升序
class Solution1 {  
private:
    TreeNode *pre = nullptr; //记录上一个节点  
public:
    bool isValidBST(TreeNode* cur) {
        if(!cur) return true;
        bool l = isValidBST(cur->left);
        if(pre && pre->val >= cur->val) return false;
        pre = cur;
        bool r = isValidBST(cur->right);
        return l & r;
    }
};
class Solution2 {  
private:
    vector<int> v;
    void traversal(TreeNode* cur) {
        if(!cur) return;
        traversal(cur->left); 
        v.push_back(cur->val);
        traversal(cur->right); 
    }
public:
    bool isValidBST(TreeNode* root) {
        traversal(root);
        int mmin = v[0];
        for(int i = 1; i < v.size(); i++) {
            if(v[i] <= v[i - 1]) return false;
        }
        return true;
    }
};
230. 二叉搜索树中第 K 小的元素✅❌
Solution1:递归取出所有元素,有额外空间开销
Solution:递归利用BST的自身性质,无额外空间开销
class Solution1 {
public:
    void travseral(TreeNode *cur, vector<int> &v) {
        if(!cur) return ;
        travseral(cur->left, v);
        v.push_back(cur->val);
        travseral(cur->right, v);
        return ;
    }
    int kthSmallest(TreeNode* root, int k) {
        vector<int> v;
        travseral(root, v);
        return v[k-1];
    }
};
class Solution2 {
private:
    int res = 0;
public:
    void travseral(TreeNode *cur, int &k) { 
        //注意不要忘了& 否则子递归修改的k不会影响父递归
        if(!cur) return ;
        travseral(cur->left, k);
        k--;
        if(k == 0) {
            res = cur->val;
            return ;
        }
        travseral(cur->right, k);
    }
    int kthSmallest(TreeNode* root, int k) {
        travseral(root, k);
        return res;
    }
};
199. 二叉树的右视图✅
简单
class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> res;
        deque<TreeNode*> dq;
        
        if(root) dq.push_back(root);
        while(!dq.empty()) {
            int size = dq.size();
            for(int i = 0; i < size; i++) {
                TreeNode *cur = dq.front();
                if(size - 1 == i) res.push_back(cur->val);
                dq.pop_front();
                if(cur->left) dq.push_back(cur->left);
                if(cur->right) dq.push_back(cur->right);
            }
        }
        return res;
    }
};
114. 二叉树展开为链表❌
钻牛角尖了,如果想遍历和展开同步,就不要递归
直接用最暴力的方法吧 把节点都存起来
class Solution {
public:
    void travseral(TreeNode* cur, vector<TreeNode*> &v) {
        if(!cur) return ;
        v.push_back(cur);
        travseral(cur->left, v);
        travseral(cur->right, v);
    }
    void flatten(TreeNode* root) {
        vector<TreeNode*> v;
        travseral(root, v);
        TreeNode *dummy = new TreeNode(0);
        TreeNode *pre = dummy;
        for(const auto &i : v) {
            pre->left = nullptr;
            pre->right = i;
            pre = pre->right;
        }
         
        root = dummy->right;
    }
};
105. 从前序与中序遍历序列构造二叉树✅
link:力扣题记之二叉树
class Solution {
public:
    TreeNode* travseral(vector<int>& preorder, int preorderBegin, int preorderEnd, vector<int>& inorder, int inorderBegin, int inorderEnd) {
        if(inorderEnd == inorderBegin) return nullptr;
        int root_val = preorder[preorderBegin];
        TreeNode *root = new TreeNode(root_val);
        if(inorderEnd == inorderBegin + 1) return root;
        
        int cutIndex = 0;
        for(cutIndex = inorderBegin; cutIndex < inorderEnd; cutIndex++) {
            if(inorder[cutIndex] == root_val) break;
        }
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = cutIndex;
        int rightInorderBegin = cutIndex + 1;
        int rightInorderEnd = inorderEnd;
        int leftPreorderBegin = preorderBegin + 1;
        int leftPreorderEnd = preorderBegin + leftInorderEnd - leftInorderBegin;
        int rightPreorderBegin = preorderBegin + leftInorderEnd - leftInorderBegin + 1;
        int rightPreorderEnd = preorderEnd;
        root->left = travseral(preorder, leftPreorderBegin, leftPreorderEnd, inorder, leftInorderBegin, leftInorderEnd);
        root->right = travseral(preorder, rightPreorderBegin, rightPreorderEnd, inorder, rightInorderBegin, rightInorderEnd);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return travseral(preorder, 0, preorder.size(), inorder, 0, inorder.size());
    }
};
※437. 路径总和 III✅
路径问题集大成者
Solution1:理清思路 注释很详细
Solution2:上面的写法参考了112. 路径总和,后来一思考发现把寻找函数写成bool类型的完全没必要啊,还搞得特别复杂,如果想要更简洁就把主函数和遍历函数写在一起,再把res变量写成全局变量,就不用在每个函数里都加上它的引用参数了(可以但没必要,现在的结构思路比较清晰)
class Solution1 {
public:
    bool findPath(TreeNode *cur, long long int sum, int &res) {
        //即使搜到了路径也不要直接返回 继续往下搜 因为可能有包含情况
        //比如【1,-2】和【1,-2,1,-1】都满足题意
        if(!sum) res++; 
        // if(!sum) return true;
        
        if(!cur->left && !cur->right) return false;
        if(cur->left) {
            sum -= cur->left->val;
            //搜到也不应该像路径总和1那样直接返回了
            //因为那是搜出一条来就行 这里是要搜出所有路径
            //以某一个节点为起点的路径可能有多条
            if(findPath(cur->left, sum, res)) {
                //因为在调用过程中搜到也不会返回true 因此这里必定返回false
            }
            sum += cur->left->val;
        }
        if(cur->right) {
            sum -= cur->right->val;
            if(findPath(cur->right, sum, res)) {
                //因为在调用过程中搜到也不会返回true 因此这里必定返回false
            }
            sum += cur->right->val;
        }
        
        return false;
    }
    void travseral(TreeNode *cur, long long int targetSum, int &res) {
        if(!cur) return ;
        //以每个节点都当成起始点搜一遍
        //此判断只针对cur节点自己就构成一条路径的情况
        //正常情况下res增加是在17行完成的 因为调用findPath函数只要不是46行
        //的情况就都最终返回41行false 因此在50行不会res++ 所以不用担心res重复++
        if(findPath(cur, targetSum - cur->val, res)) {
            res++;
            // cout << cur->val << endl;
        }
        
        travseral(cur->left, targetSum, res);
        travseral(cur->right, targetSum, res);
    }
    int pathSum(TreeNode* root, int targetSum) {
        int res = 0;
        if(!root) return 0;
        targetSum =  (long long int)targetSum; //不然处理大负数时会报错
        travseral(root, targetSum, res);
        return res;
    }
};
class Solution2 {
public:
    void findPath(TreeNode *cur, long int sum, int &res) {
        //即使搜到了路径也不要直接返回 继续往下搜 因为可能有包含情况
        //比如【1,-2】和【1,-2,1,-1】都满足题意
        if(!sum) res++;         
        if(!cur->left && !cur->right) return ; //到叶子节点了就终止
        if(cur->left) {
            sum -= cur->left->val;
            findPath(cur->left, sum, res);       
            sum += cur->left->val; //回溯
        }
        if(cur->right) {
            sum -= cur->right->val;
            findPath(cur->right, sum, res);            
            sum += cur->right->val;
        }
        
        return ;
    }
    void travseral(TreeNode *cur, long int targetSum, int &res) {
        if(!cur) return ;
        findPath(cur, targetSum - cur->val, res);
        
        travseral(cur->left, targetSum, res);
        travseral(cur->right, targetSum, res);
    }
    int pathSum(TreeNode* root, int targetSum) {
        int res = 0;
        if(!root) return 0;
        targetSum =  (long int)targetSum; //不然处理大负数时会报错
        travseral(root, targetSum, res);
        return res;
    }
};
236. 二叉树的最近公共祖先❌
从目标节点开始自底向上回溯(只能后序)一层层传过去
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root || root == p || root == q) return root;
        //找某一条路径就要在递归中及时return,而遍历整棵树就像下面这样用
        //节点来承接,递归完统一处理左右两个递归,即处理后序遍历的中间节点
        //遍历整个树不代表要把每个点都处理 比如示例一搜了三个点就返回了
        //但如果p q都是叶子节点 那就要搜完整棵树了 比如代码随想录的示例图
        TreeNode *left = lowestCommonAncestor(root->left, p, q); //左
        TreeNode *right = lowestCommonAncestor(root->right, p, q); //右
        //中:
        //这里return的就是左右两棵子树各一个p q 当下的root就是最近公共祖先
        if(left && right) return root; 
        //这里return的就是左右两棵子树最多有一棵搜到了p和q 继续往上传递直到根节点  
        if(!left && right) return right;
        else if(left  && !right) return left;
        else return nullptr;
    }
};
※124. 二叉树中的最大路径和❌
class Solution {
private:
    int sum;  
public:
    int travseral(TreeNode* cur) {
        if(!cur) return 0;
        int l = max(0, travseral(cur->left)); //如果为负值就视为0,不走该路径  
        int r = max(0, travseral(cur->right));  
        sum = max(sum, l + r + cur->val); //维护最大路径和  
        return max(l, r) + cur->val; //返回最大单侧路径和 
    }
    int maxPathSum(TreeNode* root) {
        sum = root->val;
        travseral(root);
        return sum;
    }
};
	
			
评论