当前位置: 首页 > news >正文

LeetCode树总结

​​​​​​144. 二叉树的前序遍历

递归写法很简单,不再赘述。迭代写法需要用到一个栈,因为是根->左子树->右子树的顺序进行遍历,所以弹出当前结点后要先入栈右儿子,再入栈左儿子。

/*** Definition for a binary tree node.* 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) {}* };*/
//递归版
class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;pre(root, res);return res;}void pre(TreeNode* now, vector<int>& res){if(!now) return;res.push_back(now->val);if(now->left) pre(now->left, res);if(now->right) pre(now->right, res);}
};
//迭代版
class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;if(root == nullptr) return res;stack<TreeNode*> st;st.push(root);while(st.size()){TreeNode* now = st.top();st.pop();res.push_back(now->val);if(now->right) st.push(now->right);if(now->left) st.push(now->left);}return res;}
};

94. 二叉树的中序遍历

递归写法很简单,不再赘述。迭代写法也需要一个栈,先把根入栈,然后每次都看下栈顶结点是否存在左儿子,如果有左儿子就把它的左儿子入栈,当左儿子不存在的时候需要不断出栈,直到刚出栈的这个结点它有右儿子,把它的右儿子入栈,出栈的顺序就是中序遍历的结果。

/*** Definition for a binary tree node.* 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) {}* };*/
//递归版
class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;if(root == nullptr) return res;mid(root, res);return res;}void mid(TreeNode* root, vector<int>& res){if(root->left) mid(root->left, res);res.push_back(root->val);if(root->right) mid(root->right, res);}
};
//迭代版
class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;if(root == nullptr) return res;stack<TreeNode*> st;st.push(root);while(st.size()){TreeNode* now = st.top();if(now->left) st.push(now->left);else{while(st.size()){TreeNode* top = st.top();res.push_back(top->val);st.pop();if(top->right){st.push(top->right);break;}}}}return res;}
};

145. 二叉树的后序遍历

递归写法很简单,不再赘述。迭代写法同样也需要一个栈,而且思想是和前序遍历一样的,可以仿照前序遍历迭代写法,然后对结果reverse一下就是后序遍历结果。因为reverse以后要保证是左子树->右子树->根的顺序,所以reverse前的顺序就需要是根->右子树->左子树,体现在代码上就是前序遍历递归写法中左右儿子入栈顺序交换一下,前序遍历中是先入右儿子再入左儿子,这里需要先入左儿子再入右儿子。

/*** Definition for a binary tree node.* 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) {}* };*/
//递归版
class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> res;post(root, res);return res;}void post(TreeNode* root, vector<int>& res){if(root == nullptr) return;post(root->left, res);post(root->right, res);res.push_back(root->val);}
};
//迭代版
class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> res;if(root == nullptr) return res;stack<TreeNode*> st;st.push(root);while(st.size()){TreeNode* now = st.top();st.pop();res.push_back(now->val);if(now->left) st.push(now->left);if(now->right) st.push(now->right);}reverse(res.begin(), res.end());return res;}
};

102. 二叉树的层序遍历

直接bfs就好了,但是网上看到了种dfs的写法,也挺新颖的,就是递归的时候把所在的层数也作为参数传进去,然后每到一个新层开一个空vector存储该层所有结点,由于是先序遍历,所以同层内还是按照从左向右的顺序存储的。

/*** Definition for a binary tree node.* 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) {}* };*/
//bfs写法
class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {queue<TreeNode*> q;q.push(root);q.push(nullptr);vector<int> nums;vector<vector<int>> res;if(root == nullptr) return res;while(q.size()){TreeNode* now = q.front();q.pop();if(!now){res.push_back(nums);if(q.size())q.push(nullptr);nums.clear();}else{nums.push_back(now->val);if(now->left) q.push(now->left);if(now->right) q.push(now->right);}}return res;}
};

662. 二叉树最大宽度

还是一个bfs,然后将同层的结点维护在一个vector里,不过这里要对结点进行编号,可以用二叉树的那种编号方式,左儿子和右儿子分别是父节点<<1和父节点<<1|1,然后维护最大的两编号差值,但是这道题最多有3000个结点,所以编号很容易溢出,同时题目给出了答案不会超出32位int范围,也就是说虽然作差的两个编号很大,但是差值并不大。所以可以借助模运算,只要模一个比int最大值大的数就行了,为了方便起见直接用unsigned long long存储所有数据。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:int widthOfBinaryTree(TreeNode* root) {queue<pair<TreeNode*, unsigned long long>> q;q.push(make_pair(root, 1));q.push(make_pair(nullptr, 0));vector<unsigned long long> nums;unsigned long long res = 0;while(q.size()){TreeNode* now = q.front().first;unsigned long long id = q.front().second;q.pop();if(id) nums.push_back(id);if(now){if(now->left) q.push(make_pair(now->left, id<<1));if(now->right) q.push(make_pair(now->right, id<<1|1));}else{if(q.size()) q.push(make_pair(nullptr, 0));if(nums.size() >= 1)res = max(res, nums[nums.size()-1]-nums[0]+1);nums.clear();}}return res;}
};

101. 对称二叉树

这道题主要还是要把握住子结构,有两种方法,递归或者迭代,无论哪种方法思想其实都差不多,一棵树关于中轴对称需要它的左右子树关于中轴对称,所以这道题就转化为判断两颗树是否关于中轴对称。两棵树是否对称有两个条件,首先两个根上的值需要相同,其次就是树1的左子树要和树2的右子树对称,树1的右子树要和树2的左子树对称。这道题的子结构就出来了,递归的话就很好写了。迭代法的话需要一个队列,类似bfs的思想,不过现在是在两棵树上同时bfs,结点都放在一个队列里,每次入队出队都是两棵树一起,所以两棵树的对应结点会在队列中相邻,然后看一下这相邻的两结点值是否相同,如果出现了不同那就return false,入队的时候树1入左儿子,紧接着树2要入右儿子,树1入右儿子,紧接着树2要入左儿子。

/*** Definition for a binary tree node.* 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) {}* };*/
//递归版
class Solution {
public:bool isSymmetric(TreeNode* root) {return check(root, root);}bool check(TreeNode* tr1, TreeNode* tr2){if(!tr1 && !tr2) return true;if(!tr1 || !tr2) return false;return tr1->val == tr2->val && check(tr1->right, tr2->left) && check(tr1->left, tr2->right);}
};
//迭代版
class Solution {
public:bool isSymmetric(TreeNode* root) {queue<TreeNode*> q;q.push(root->left);q.push(root->right);while(q.size()){TreeNode* tr1 = q.front();q.pop();TreeNode* tr2 = q.front();q.pop();if(!tr1 && !tr2) continue;if(!tr1 || !tr2) return false;if(tr1->val != tr2->val) return false;q.push(tr1->left);q.push(tr2->right);q.push(tr1->right);q.push(tr2->left);}return true;}
};

104. 二叉树的最大深度

递归求解,递归左子树和递归右子树取最大值再+1作为返回值。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:int maxDepth(TreeNode* root) {if(root == nullptr) return 0;return max(maxDepth(root->left), maxDepth(root->right))+1;}
};

110. 平衡二叉树

写一个dfs求一下树的深度,然后比较左右子树深度就行了。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:bool ans = true;bool isBalanced(TreeNode* root) {dfs(root);return ans;}int dfs(TreeNode* root){if(root == nullptr) return 0;int h1 = dfs(root->left);int h2 = dfs(root->right);if(abs(h1-h2) > 1) ans = false;return max(h1, h2)+1;}
};

226. 翻转二叉树

比较简单,直接递归swap左右儿子就行了。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:TreeNode* invertTree(TreeNode* root) {dfs(root);return root;}void dfs(TreeNode* root){if(root == nullptr) return;swap(root->left, root->right);dfs(root->left);dfs(root->right);}
};

572. 另一棵树的子树

对于每个结点都进行check,判断它是否和给出的子树相同,check的过程也是一个递归。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:bool isSubtree(TreeNode* root, TreeNode* subRoot) {if(check(root, subRoot)) return true;if(root == nullptr) return false;if(isSubtree(root->left, subRoot)) return true;if(isSubtree(root->right, subRoot)) return true;return false;}bool check(TreeNode* root, TreeNode* subRoot){if(!root && !subRoot) return true;if(!root || !subRoot) return false;if(root->val != subRoot->val) return false;return check(root->left, subRoot->left) && check(root->right, subRoot->right);}};

112. 路径总和

dfs一遍就行了,用参数维护路径上的加和。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:bool res = false;bool hasPathSum(TreeNode* root, int targetSum) {if(!root) return false;dfs(root, root->val, targetSum);return res;}void dfs(TreeNode* root, int now, int target){if(res) return;if(now == target && !root->left && !root->right){res = true;return;}if(root->left) dfs(root->left, now+root->left->val, target);if(root->right) dfs(root->right, now+root->right->val, target);}
};

98. 验证二叉搜索树

三种方法,最好写的方法就是看下中序序列是否升序,因为一棵树是BST等价于它的中序序列升序。第二种方法是写个dfs,dfs返回的是当前子树中最大值和最小值,存储在一个pair中,然后每个结点都判断一下是否该结点值是否大于左子树中的最大值并且小于右子树中的最小值。第三种方法也是dfs,和第二种方法的区别在于第二种方法是站在当前结点的角度,根据左右子树情况判断当前结点是否合法,第三种方法是站在左右子树的角度,由于其祖先结点会施加一系列最值约束,判断每个结点是否满足其祖先带来的约束就行了。

/*** Definition for a binary tree node.* 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) {}* };*/
//法一
class Solution {
public:bool res = true;long long last = -0x3f3f3f3f3f3f3f3f;bool isValidBST(TreeNode* root) {if(root == nullptr) return false;mid(root);return res;}void mid(TreeNode* root){if(!res) return;if(root->left) mid(root->left);if(last >= root->val)res = false;last = root->val;if(root->right) mid(root->right);}
};
//法二
class Solution {
public:bool res = true;bool isValidBST(TreeNode* root) {dfs(root);return res;}pair<int, int> dfs(TreeNode* root){if(!res) return make_pair(0, 0);int mn = root->val, mx = root->val;if(root->left){pair<int, int> l = dfs(root->left);if(root->val <= l.second) res = false;mn = min(mn, l.first);}if(root->right){pair<int, int> r = dfs(root->right);  if(root->val >= r.first) res = false;mx = max(mx, r.second);}return make_pair(mn, mx);}
};
//法三
class Solution {
public:bool isValidBST(TreeNode* root) {return dfs(root, nullptr, nullptr);}bool dfs(TreeNode* root, TreeNode* min, TreeNode* max) {if(root == nullptr) return true;if(min != nullptr && root->val <= min->val) return false;if(max != nullptr && root->val >= max->val) return false;return dfs(root->right, root, max) && dfs(root->left, min, root);}
};

剑指 Offer 54. 二叉搜索树的第k大节点

因为树是一颗BST,所以它的中序序列肯定升序,所以可以从中序序列里拿到第k大结点。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:int tmp, res;int findTargetNode(TreeNode* root, int cnt) {tmp = cnt;mid(root);return res;}void mid(TreeNode* root){if(tmp == 0) return;if(root->right) mid(root->right);tmp--;if(tmp == 0) res = root->val;if(root->left) mid(root->left);}
};

230. 二叉搜索树中第K小的元素

和找第k大一样的思路,也是从中序序列入手。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:int tmp, res;int kthSmallest(TreeNode* root, int cnt) {tmp = cnt;mid(root);return res;}void mid(TreeNode* root){if(tmp == 0) return;if(root->left) mid(root->left);tmp--;if(tmp == 0) res = root->val;if(root->right) mid(root->right);}
};

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

先dfs找到待删除的结点,然后如果它左儿子为空,那就把待删除结点替换为右儿子,如果右儿子为空,就把待删除结点替换为左儿子,如果都不为空,那可以找到左子树中不断找右儿子,找到最靠右的那个结点,然后把待删除结点右儿子接到它下面,之后令待删除结点的左儿子替代待删除结点,总之要保证中序序列仍然有序。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {dfs(root, key);return root;}void dfs(TreeNode*& now, int key){if(now == nullptr) return;if(now->val > key) dfs(now->left, key);else if(now->val < key) dfs(now->right, key);else{if(!now->left) now = now->right;else if(!now->right) now = now->left;else{TreeNode* p = now->left;while(p->right) p = p->right;p->right = now->right;now = now->left;}            }}
};

958. 二叉树的完全性检验

跑一个bfs就行了,不过空结点也要入队,因为完全二叉树的层序序列一定都是非空结点->......->非空结点->空结点->......->空结点这样的顺序,所以在bfs过程中记录上一个访问结点,然后看相邻两结点是否会出现空结点->非空结点这样的异常情况。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:bool isCompleteTree(TreeNode* root) {if(root == nullptr) return false;queue<TreeNode*> q;q.push(root);TreeNode* last = root;while(q.size()){TreeNode* now = q.front();q.pop();if(now && !last) return false;if(now){q.push(now->left);q.push(now->right);}last = now;}return true;}
};

剑指 Offer 26. 树的子结构

类似LeetCode572,然后也只需要更改一下check函数中的条件就行了。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:bool isSubStructure(TreeNode* A, TreeNode* B) {if(A == nullptr || B == nullptr) return false;if(check(A, B)) return true;if(A == nullptr) return false;if(isSubStructure(A->left, B)) return true;if(isSubStructure(A->right, B)) return true;return false;}bool check(TreeNode* root, TreeNode* subRoot){if(!subRoot) return true;if(!root) return false;if(root->val != subRoot->val) return false;return check(root->left, subRoot->left) && check(root->right, subRoot->right);}
};

113.路径总和 II

维护一个全局的vector记录dfs路径上的数字序列,然后当路经总和为目标值时添加进结果vector就行了。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:vector<int> nums;vector<vector<int>> res;vector<vector<int>> pathSum(TreeNode* root, int targetSum) {if(root == nullptr) return res;nums.push_back(root->val);dfs(root, targetSum, root->val);return res;}void dfs(TreeNode* now, int target, int sum){if(target == sum && !now->left && !now->right) res.push_back(nums);if(now->left){nums.push_back(now->left->val);dfs(now->left, target, sum+now->left->val);nums.pop_back();}if(now->right){nums.push_back(now->right->val);dfs(now->right, target, sum+now->right->val);nums.pop_back();}}
};

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

按层次bfs遍历一下就行了,同时维护一个flag标记,代表是否要对该层反转。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:vector<vector<int>> zigzagLevelOrder(TreeNode* root) {queue<TreeNode*> q;q.push(root);q.push(nullptr);bool flag = false;vector<vector<int>> ans;if(root == nullptr) return ans;vector<int> t;while(q.size()){TreeNode* now = q.front();q.pop();if(now == nullptr){if(q.size())q.push(nullptr);if(flag) reverse(t.begin(), t.end());flag = !flag;ans.push_back(t);t.clear();}else{t.push_back(now->val);if(now->left) q.push(now->left);if(now->right) q.push(now->right);}}return ans;}
};

199. 二叉树的右视图

和上一题思路一样,也是一个按层次bfs,取该层最后一个结点放入结果vector中。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:vector<int> rightSideView(TreeNode* root) {queue<TreeNode*> q;q.push(root);q.push(nullptr);vector<int> ans;if(root == nullptr) return ans;vector<int> t;while(q.size()){TreeNode* now = q.front();q.pop();if(now == nullptr){if(q.size())q.push(nullptr);ans.push_back(t[t.size()-1]);t.clear();}else{t.push_back(now->val);if(now->left) q.push(now->left);if(now->right) q.push(now->right);}}return ans;}
};

124. 二叉树中的最大路径和

类似树的直径,可以两遍dfs或者树形dp解决,这里由于给的树并非无向图,所以两遍dfs不好实现,可以用哈希映射指针+树形dp来求解。另外这里可以对空间进行一次优化,dfs直接返回最大值,然后次大值在过程中求出来。

/*** Definition for a binary tree node.* 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) {}* };*/
//哈希映射指针版
class Solution {
public:unordered_map<TreeNode*, int> dp1;unordered_map<TreeNode*, int> dp2;int ans = -0x3f3f3f3f;int maxPathSum(TreeNode* root) {dfs(root);return ans;}void dfs(TreeNode* now){if(now->left) dfs(now->left);if(now->right) dfs(now->right);dp1[now] = dp2[now] = 0;if(now->left){if(now->left->val+dp1[now->left] >= dp1[now]){dp2[now] = dp1[now];dp1[now] = now->left->val+dp1[now->left];}else if(now->left->val+dp1[now->left] > dp2[now])dp2[now] = now->left->val+dp1[now->left];}if(now->right){if(now->right->val+dp1[now->right] >= dp1[now]){dp2[now] = dp1[now];dp1[now] = now->right->val+dp1[now->right];}else if(now->right->val+dp1[now->right] > dp2[now])dp2[now] = now->right->val+dp1[now->right];}ans = max(ans, dp1[now]+dp2[now]+now->val);}
};
//空间优化版
class Solution {
public:int ans = -0x3f3f3f3f;int maxPathSum(TreeNode* root) {dfs(root);return ans;}int dfs(TreeNode* now){vector<int> a;if(now->left) a.push_back(dfs(now->left)+now->left->val);if(now->right) a.push_back(dfs(now->right)+now->right->val);int dp1 = 0, dp2 = 0;for(int i = 0; i < a.size(); i++){if(a[i] >= dp1){dp2 = dp1;dp1 = a[i];}else if(a[i] > dp2) dp2 = a[i];}ans = max(ans, dp1+dp2+now->val);return dp1;}
};

543. 二叉树的直径

和上一题类似,仍然是用树形dp解决。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:int ans = -0xf3f3f3f3f;int diameterOfBinaryTree(TreeNode* root) {dfs(root);return ans;}int dfs(TreeNode* now){int l = 0, r = 0;if(now->left) l = dfs(now->left)+1;if(now->right) r = dfs(now->right)+1;int dp1 = max(l, r), dp2 = min(l, r);ans = max(ans, dp1+dp2);return dp1;}
};

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

单次询问不需要用树上倍增,暴力求LCA就行了,先dfs看下两结点深度,然后跳到相同深度以后同步跳。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:unordered_map<TreeNode*, TreeNode*> fa;int hp, hq;TreeNode* pp, * qq;TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {pp = p, qq = q;dfs(root, 1);if(hp > hq){swap(hp, hq);swap(p, q);}while(hq != hp){q = fa[q];hq--;}while(p != q){p = fa[p];q = fa[q];}return p;}void dfs(TreeNode* now, int level){if(now == pp) hp = level;if(now == qq) hq = level;if(now->left){fa[now->left] = now;dfs(now->left, level+1);}if(now->right){fa[now->right] = now;dfs(now->right, level+1);}}
};

129. 求根节点到叶节点数字之和

一遍dfs就出来了,参数维护路径上的数字。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:long long ans;int sumNumbers(TreeNode* root) {dfs(root, root->val);return ans;}void dfs(TreeNode* now, long long num){if(!now->left && !now->right) ans += num;if(now->left) dfs(now->left, num*10+now->left->val);if(now->right) dfs(now->right, num*10+now->right->val);}
};

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

思路比较简单,用递归实现就行了。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {if(preorder.size() == 0) return nullptr;int val = preorder[0];TreeNode* root = new TreeNode(val);vector<int> lin, rin, lpre, rpre;bool flag = false;for(int i = 0; i < inorder.size(); i++){if(inorder[i] == val){flag = true;continue;}    if(!flag) lin.push_back(inorder[i]);else rin.push_back(inorder[i]);}for(int i = 1; i < preorder.size(); i++){if(i <= lin.size()) lpre.push_back(preorder[i]);else rpre.push_back(preorder[i]);}root->left = buildTree(lpre, lin);root->right = buildTree(rpre, rin);return root;}
};

297. 二叉树的序列化与反序列化

题目要求将二叉树转为字符串然后再根据该字符串新建一棵相同的树,可以考虑bfs,在层次遍历的过程中记录下来该树,然后反序列化的时候类似的操作,也是一个bfs的过程,具体怎么设计无所谓,保证能够恢复出来就行。这道题有两点要特别注意,首先是结点值会包含负值,所以字符串和数字转换时要注意一下,其次是对于字符串的拼接,这点操作不当可能会导致MLE,之前习惯了str = str + "abc"这样的写法,但这种写法会额外创建字符串对象(str + "abc"),

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Codec {
public:stringstream ss;string stoi(int& a){ss.clear();ss.str("");string res;ss << a;ss >> res;return res;}// Encodes a tree to a single string.string serialize(TreeNode* root) {if(root == NULL) return "";queue<TreeNode*> q;q.push(root);string res = "";while(q.size()){TreeNode* now = q.front();q.pop();if(now){q.push(now->left);q.push(now->right);res += stoi(now->val)+'#';}else res += "N#";}return res;}TreeNode* getNext(int& pos, string& data){int val = 0;bool flag = false;while(pos < data.length() && data[pos] != '#'){if(data[pos] == 'N'){pos += 2;return NULL;}else if(data[pos] == '-'){flag = true;pos++;}else{val = val*10+data[pos]-'0';pos++;}}pos++;if(flag) val = -val;TreeNode* node = new TreeNode(val);return node;}// Decodes your encoded data to tree.TreeNode* deserialize(string data) {if(data == "") return NULL;int pos = 0;queue<TreeNode*> q;TreeNode* root = getNext(pos, data);q.push(root);while(q.size()){TreeNode* now = q.front();q.pop();now->left = getNext(pos, data);now->right = getNext(pos, data);if(now->left) q.push(now->left);if(now->right) q.push(now->right);}return root;}
};// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));

114. 二叉树展开为链表

典型的可以用递归解决的问题,对于一棵树,先递归处理其左右子树,将其左右子树展开为链表,dfs返回值应该是展成链表后最后一个结点的指针,用两个指针l、r分别记录左右子树末尾节点,这样对于当前这棵树now就可以令l->right = now->right, now->right = now->left, now->left = nullptr,此时就完成将now树展为链表操作了。

/*** Definition for a binary tree node.* 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) {}* };*/
class Solution {
public:void flatten(TreeNode* root) {if(root == nullptr) return;dfs(root);}TreeNode* dfs(TreeNode* now){if(now == nullptr) return nullptr;TreeNode* l = dfs(now->left);TreeNode* r = dfs(now->right);if(l && !r){now->right = now->left;now->left = nullptr;return l;}else if(l && r){l->right = now->right;now->right = now->left;now->left = nullptr;return r;}else if(!l && !r) return now;else return r;}
};

剑指 Offer 36. 二叉搜索树与双向链表

这道题有点类似上一道把二叉树展成链表,也是利用递归的思想,如果左子树和右子树已经是有序的双向链表,那就很好处理了,左中右一拼接就行了。

/*
// Definition for a Node.
class Node {
public:int val;Node* left;Node* right;Node() {}Node(int _val) {val = _val;left = NULL;right = NULL;}Node(int _val, Node* _left, Node* _right) {val = _val;left = _left;right = _right;}
};
*/
class Solution {
public:Node* treeToDoublyList(Node* root) {return dfs(root).first;}pair<Node*, Node*> dfs(Node* now){if(now == NULL) return make_pair((Node*)NULL, (Node*)NULL);pair<Node*, Node*> l = dfs(now->left);pair<Node*, Node*> r = dfs(now->right);Node* ls = l.first, * le = l.second;Node* rs = r.first, * re = r.second;if(!ls && !rs){now->left = now;now->right = now;return make_pair(now, now);}else if(!ls && rs){rs->left = now;re->right = now;now->right = rs;now->left = re;return make_pair(now, re);}else if(ls && !rs){ls->left = now;le->right = now;now->left = le;now->right = ls;return make_pair(ls, now);}else{now->left = le;now->right = rs;le->right = now;rs->left = now;ls->left = re;re->right = ls;return make_pair(ls, re);}}
};

相关文章:

LeetCode树总结

​​​​​​144. 二叉树的前序遍历 递归写法很简单&#xff0c;不再赘述。迭代写法需要用到一个栈&#xff0c;因为是根->左子树->右子树的顺序进行遍历&#xff0c;所以弹出当前结点后要先入栈右儿子&#xff0c;再入栈左儿子。 /*** Definition for a binary tree n…...

AI专题:冬渐去、春将来,待看,AI 开花,数据挂果,可控链潮起

今天分享的是AI 系列深度研究报告&#xff1a;《AI专题&#xff1a;冬渐去、春将来&#xff0c;待看&#xff0c;AI 开花&#xff0c;数据挂果&#xff0c;可控链潮起》。 &#xff08;报告出品方&#xff1a;AVIC&#xff09; 报告共计&#xff1a;36页 行业概览:2023年呈稳…...

Netty源码系列 之 EventLoop run()方法 源码

EventLoop[实现类为NioEventLoop&#xff0c;我们研究NioEventLoop即可] EventLoop是一个单线程的线程池 核心作用&#xff1a;处理执行IO操作&#xff08;accept&#xff0c;read&#xff0c;write事件&#xff09;&#xff0c;普通任务&#xff0c;定时任务 EventLoop封装…...

ChatGPT 4.0 升级指南, ChatGPT Plus(GPT 4.0) 有何优势?

1.ChatGPT 是什么&#xff1f; ChatGPT 是由 OpenAI 开发的一种基于人工智能的聊天机器人&#xff0c;它基于强大的语言处理模型 GPT&#xff08;Generative Pre-trained Transformer&#xff09;构建。它能够理解人类语言&#xff0c;可以为我们解决实际的问题。 ChatGPT 4.…...

springboot157基于springboot的线上辅导班系统的开发与设计

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…...

【机器学习】机器学习简单入门

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;matplotlib &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…...

考研数据结构笔记(1)

数据结构&#xff08;1&#xff09; 数据结构在学什么&#xff1f;数据结构的基本概念基本概念三要素逻辑结构集合线性结构树形结构图结构 物理结构&#xff08;存储结构&#xff09;顺序存储链式存储索引存储散列存储重点 数据的运算 算法的基本概念什么是算法算法的五个特性有…...

【深度学习理论】持续更新

文章目录 1.统计学习理论 1.统计学习理论 统计学习理论&#xff0c;一款适合零成本搞深度学习的大冤种的方向 从人类学习到机器学习的对比&#xff08;学习的过程分为归纳和演绎 &#xff09;&#xff0c;引出泛化和过拟合的概念。 如何表示归纳的函数规律呢&#xff1f;以监督…...

npm ERR! reason: certificate has expired(淘宝镜像过期)

npm ERR! request to https://registry.npm.taobao.org/yauzl/-/yauzl-2.4.1.tgz failed, reason: certificate has expired 今天在执行npm install命令时&#xff0c;报错百度了下是淘宝证书过期原因 解决方法一 执行下面两个命令再进行npm install即可 npm cache clean --…...

“极简壁纸“爬虫JS逆向·实战

文章目录 声明目标分析确定目标目标检索 代码补全完整代码 爬虫逻辑完整代码 运行结果 声明 本教程只用于交流学习&#xff0c;不可用于商业用途&#xff0c;不可对目标网站进行破坏性请求&#xff0c;请遵守相关法律法规。 目标分析 确定目标 获取图片下载链接 目标检索…...

Django通过Json配置文件分配多个定时任务

def load_config():with open("rule.json", rb)as f:config json.load(f)return configdef job(task_name, config, time_interval):# ... 通过task_name判断进行操作if task_name get_data_times:passdef main():config load_config()for task_name, task_value…...

C++ 搜索二叉树的删除

首先查找元素是否在二叉搜索树中&#xff0c;如果不存在&#xff0c;则返回 要删除的结点可能分下面四种情况&#xff1a; a. 要删除的结点无孩子结点 b. 要删除的结点只有左孩子结点 c. 要删除的结点只有右孩子结点 d. 要删除的结点有左、右孩子结点 看起来有待删除节点有4中…...

构建中国人自己的私人GPT—支持中文

上一篇已经讲解了如何构建自己的私人GPT&#xff0c;这一篇主要讲如何让GPT支持中文。 privateGPT 本地部署目前只支持基于llama.cpp 的 gguf格式模型&#xff0c;GGUF 是 llama.cpp 团队于 2023 年 8 月 21 日推出的一种新格式。它是 GGML 的替代品&#xff0c;llama.cpp 不再…...

elementui 回到顶部报错

<template>Scroll down to see the bottom-right button.<el-backtop target".page-component__scroll .el-scrollbar__wrap"></el-backtop> </template> 使用element的Backtop 回到顶部组件的伙伴们&#xff0c;把官网代码复制到页面使用时…...

go-carbon v2.3.8 发布,轻量级、语义化、对开发者友好的 golang 时间处理库

carbon 是一个轻量级、语义化、对开发者友好的 golang 时间处理库&#xff0c;支持链式调用。 目前已被 awesome-go 收录&#xff0c;如果您觉得不错&#xff0c;请给个 star 吧 github.com/golang-module/carbon gitee.com/golang-module/carbon 安装使用 Golang 版本大于…...

【详解】斗地主随机发牌项目

目录 前言&#xff1a; 1.初始化牌 2.洗牌 3.揭牌 总代码&#xff1a; Card类&#xff1a; CardGame类&#xff1a; Main类&#xff1a; 结语&#xff1a; 前言&#xff1a; 斗地主是全国范围内的一种桌面游戏&#xff0c;本节我们来实现一下斗地主中的简单初始化牌、…...

多账号运营为什么要使用动态住宅代理IP?

对于跨境有多账号运营需求的企业来说&#xff0c;选择正确类型的代理IP对于平稳运行至关重要。但最适合这项工作的代理类型是什么&#xff1f;为了更好地管理不同平台上的多个账户并优化成本&#xff0c;您可以选择动态住宅代理。 一、什么是动态住宅代理 动态住宅代理IP是互联…...

[C++] 如何使用Visual Studio 2022 + QT6创建桌面应用

安装Visual Studio 2022和C环境 [Visual Studio] 基础教程 - Window10下如何安装VS 2022社区版_visual studio 2022 社区版-CSDN博客 安装QT6开源版 下载开源版本QT Try Qt | 开发应用程序和嵌入式系统 | Qt Open Source Development | Open Source License | Qt 下载完成&…...

Arduino 推出基于乐鑫 ESP32-S3 的 STEM 教育机器人

Arduino Alvik 是 Arduino Education 推出的一款新型机器人&#xff0c;可作为一种跨学科工具&#xff0c;为当前教育和未来机器人世界筑起连接的桥梁。Hackster 的 Gareth Halfacree 表示&#xff1a;“Alvik 的设计灵感来自 Arduino 简化复杂技术的理念&#xff0c;同时它也 …...

Blender使用Rigify和Game Rig Tool基础

做动画需要的几个简要步骤&#xff1a; 1.建模 2.绑定骨骼 3.绘制权重 4.动画 1.Rigify是干嘛用的&#xff1f; 》 绑定骨骼 2.Game Rig Tool干嘛用的&#xff1f; 》 修复Rigify绑定骨骼做的动画导入游戏引擎的问题&#xff0c;如果Rigify自身修复了就不需要这个插件了&#…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...