来吧~继续奔跑!从左叶子之和开始。(本篇建议读完上篇力扣二叉树专题Ⅰ后服用)
二叉树属性
左叶子之和
迭代法
int sumOfLeftLeaves(TreeNode* root) {
stack<TreeNode*> st;
int sum = 0;
if(!root) return sum;
st.push(root);
while(!st.empty()){
TreeNode* cur = st.top();
st.pop();
if(cur->left){
TreeNode* tmp = cur->left;
if(!tmp->left && !tmp->right) sum += tmp->val;
st.push(tmp);
}
if(cur->right) st.push(cur->right);
}
return sum;
}
递归法
感觉还是没有很懂,写了一个第一个return就直接返回了。写的啥玩意?也叫递归?
确实嗷,好像都是左右结点用来递归(调用本函数),中间的结点用来执行一些操作(要么最简单的输出结果啊、要么压栈啊、入队啊、计算和啊什么的)而且此处要写最关键的逻辑。就比如本题,关键逻辑是:若该结点的左子不空,且该左子没有左子和右子,则找到了一个左叶子。(通过该结点是无法判断的,必须要通过父结点来判断)
int sumOfLeftLeaves(TreeNode* root) {
if(!root) return 0;
int leftNodes = sumOfLeftLeaves(root->left);//左
int rightNodes = sumOfLeftLeaves(root->right);//右
int midNodes = 0;//每次都要记得置零
if(root->left && !root->left->left && !root->left->right) midNodes = root->left->val; //中
return midNodes + leftNodes + rightNodes;//用该结点的左子+左子树的左子+右子树的左子=整棵树的左子之和
}
找树左下角的值
迭代法
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> q;
q.push(root);
int res = root->val;
while(!q.empty()){
int s = q.size();
for(int i = 0; i < s; i++){//每一层
TreeNode* cur = q.front();
q.pop();
if(i == 0) res = cur->val;
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
}
return res;
}
递归法
最深的一定是最下面那一层,怎么找到最左边的那个呢?无论中左右还是左右中都可以,只要保证左在前,就可以了。
相应的,如果要最深的一层的最右值,让右在前就可以了。
关键是把握好核心,就是找到最深的,更新层数和最左的结点值。
int maxDepth = INT_MIN;
int res;
void solve(TreeNode* cur, int leftDepth){
if(cur->left) solve(cur->left, leftDepth+1);
if(cur->right) solve(cur->right, leftDepth+1);
if(cur && !cur->left && !cur->right && leftDepth > maxDepth){
maxDepth = leftDepth;
res = cur->val;
}
}
int findBottomLeftValue(TreeNode* root) {
solve(root, 0);
return res;
}
路径总和
递归法
纪念一下我独立写出的第一个正确的递归~(虽然很丑但是希望大家不要嫌弃QAQ)
虽然笨拙了点,但好歹是对的(不像之前那个一下就返回的假递归呜呜)
int target;
vector<int> v;
void solve(TreeNode* cur, int sum){
if(cur && !cur->left && !cur->right){
v.push_back(sum+cur->val);
}
if(cur->left) solve(cur->left, sum+cur->val);
if(cur->right) solve(cur->right, sum+cur->val);
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
target = targetSum;
solve(root, 0);
for(int i = 0; i < v.size(); i++){
if(targetSum == v[i]) return true;
}
return false;
}
再来看看大佬写的递归,那不比我简单许多?
直接传一个targetSum,若是减到零了就是正好;否则就是没有。
bool solve(TreeNode* cur, int sum){
if(cur && !cur->left && !cur->right && sum == 0) return true;
if(cur->left && solve(cur->left, sum - cur->left->val)) return true;
if(cur->right && solve(cur->right, sum - cur->right->val)) return true;
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
return solve(root, targetSum - root->val);
}
关于返回值的问题,总结是:如果要操作整棵树,就void;如果只需要找到其中一条路,就可以要返回值。
还有一个精简版(但是用了这么多方法好像也了解了这么写的缘故)
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
if(!root->left && !root->right && targetSum==root->val) return true;
return hasPathSum(root->left, targetSum-root->val) || hasPathSum(root->right, targetSum-root->val);
}
迭代法
需要压栈的时候压一个pair,记得用node.first和node.second指代前后两个元素,别和->弄混了。
这个pair前一个是遍历到的结点,后一个是从根结点到该结点的val之和。
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
stack<pair<TreeNode*, int>> st;
st.push(make_pair(root, root->val));
while(!st.empty()){
pair<TreeNode*, int> node = st.top();
st.pop();
if(!node.first->left && !node.first->right && node.second == targetSum) return true;
if(node.first->left) st.push(make_pair(node.first->left, node.second+node.first->left->val));
if(node.first->right) st.push(make_pair(node.first->right, node.second+node.first->right->val));
}
return false;
}
路径总和Ⅱ
递归法
自己写的~(嘿嘿越来越像那么回事了)其实是照着二叉树的所有路径那题模仿的。
vector<vector<int>> res;
void solve(TreeNode* root, vector<int>& path, int sum){
if(root && !root->left && !root->right && sum==0) res.push_back(path);
if(root->left){
int tmp = root->left->val;
path.push_back(tmp);
solve(root->left, path, sum-tmp);
path.pop_back();
}
if(root->right){
int tmp = root->right->val;
path.push_back(tmp);
solve(root->right, path, sum-tmp);
path.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
if(!root) return res;
vector<int> path;
path.push_back(root->val);
int sum = targetSum;
solve(root, path, sum-root->val);
return res;
}
迭代法
如果用栈的话,我想过了,要带三个参数:一个vector记录路径,一个当前结点,一个路径之和;但是如果不怕麻烦的话,路径之和可以用vector算出来,那么每次遍历到叶子结点都要算一遍(先这么写着吧作为初步思路)
不要忘了回溯呀(因为不是找到一个路径就停止,而是要找到所有的路径,那就不能停止,要回头)
vector<vector<int>> res;
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
stack<TreeNode*> nodest;
stack<vector<int>> pathst;
if(!root) return res;
nodest.push(root);
vector<int> v;
v.push_back(root->val);
pathst.push(v);
while(!nodest.empty()){
TreeNode* cur = nodest.top(); nodest.pop();
vector<int> path = pathst.top(); pathst.pop();
if(!cur->left && !cur->right) {
int sum = 0;
for(int i = 0; i < path.size(); i++) sum += path[i];
if(sum == targetSum) res.push_back(path);
}
if(cur->left){
nodest.push(cur->left);
path.push_back(cur->left->val);
pathst.push(path);
path.pop_back();
}
if(cur->right){
nodest.push(cur->right);
path.push_back(cur->right->val);
pathst.push(path);
path.pop_back();
}
}
return res;
}
构造二叉树
从中序与后续遍历序列构造二叉树
这里细想一下还是很熟悉的,因为曾经认认真真地没看题解想过,也敲过,所以总有些递归的手感在。
要注意的是,若要修改或者构造(总之就是改变)二叉树的时候,不要传递指针,直接写一个函数返回指针就好。
map是用来对应中序中值和位置的。(后面继续学习实现map的红黑树和实现unordered_map的哈希表)因为前/后序中根的位置好找(不是第一个就是最后一个)
还有就是最开始的那句范围判断别忘啦~
unordered_map<int, int> pos;
TreeNode* solve(int inleft, int inright, int posleft, int posright, vector<int> inorder, vector<int> postorder){
if(inleft > inright || posleft > posright) return NULL;
int rootvalue = postorder[posright];
int rootpos = pos[rootvalue];
TreeNode* root = new TreeNode(rootvalue);
root->left = solve(inleft, rootpos-1, posleft, posleft+rootpos-inleft-1, inorder, postorder);
root->right = solve(rootpos+1, inright, posleft+rootpos-inleft, posright-1, inorder, postorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int s = inorder.size();
if(s==0) return NULL;
for(int i = 0; i < s; i++){
pos[inorder[i]] = i;
}
return solve(0, s-1, 0, s-1, inorder, postorder);
}
从前序与中序遍历序列构造二叉树
unordered_map<int, int> pos;
TreeNode* solve(int preleft, int preright, int inleft, int inright, vector<int> preorder, vector<int> inorder){
if(preleft > preright || inleft > inright) return NULL;
int rootvalue = preorder[preleft];
int rootpos = pos[rootvalue];
TreeNode* root = new TreeNode(rootvalue);
root->left = solve(preleft+1, preleft+rootpos-inleft, inleft, rootpos-1, preorder, inorder);
root->right = solve(preleft+rootpos-inleft+1, preright, rootpos+1, inright, preorder, inorder);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int s = preorder.size();
for(int i = 0; i < s; i++) pos[inorder[i]] = i;
return solve(0, s-1, 0, s-1, preorder, inorder);
}
ps:必须要有前序和后序之一,但是中序必须要有,因为没有中序就无法分割左右。
最大二叉树
欸嘿,掌握了方法,写起来就很顺了!
unordered_map<int, int> pos;
TreeNode* solve(int numsleft, int numsright, vector<int> nums){
if(numsleft > numsright) return NULL;
int m = INT_MIN;
for(int i = numsleft; i <= numsright; i++) m = max(m, nums[i]);
int max_pos = pos[m];
TreeNode* root = new TreeNode(m);
root->left = solve(numsleft, max_pos-1, nums);
root->right = solve(max_pos+1, numsright, nums);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
int s = nums.size();
if(s == 0) return NULL;
for(int i = 0; i < s; i++) pos[nums[i]] = i;
return solve(0, s-1, nums);
}
合并二叉树
递归法
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(!root1 && !root2) return NULL;
if(!root1) return root2;
if(!root2) return root1;
TreeNode* root = new TreeNode();
root->val = root1->val + root2->val;
root->left = mergeTrees(root1->left, root2->left);
root->right = mergeTrees(root1->right, root2->right);
return root;
}
夭寿啦夭寿啦怎么递归这么容易就写出来了!看起来是掌握了秘法!(多刷题,你也可以!)就按照我的刷题顺序来(这都是前人走过的路摸出来的经验和便捷法门)
当然这题也可以迭代,每次把两棵树同位置的结点入队就行啦!把root2的那棵树叠加到root1的身上就好啦,还是写一下吧~
迭代法
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(!root1) return root2;
if(!root2) return root1;
queue<TreeNode*> q;
q.push(root1);
q.push(root2);
while(!q.empty()){
TreeNode* cur1 = q.front(); q.pop();
TreeNode* cur2 = q.front(); q.pop();
cur1->val += cur2->val;
if(cur1->left && cur2->left){
q.push(cur1->left);
q.push(cur2->left);
}
if(cur1->right && cur2->right){
q.push(cur1->right);
q.push(cur2->right);
}
if(!cur1->left && cur2->left) cur1->left = cur2->left;
if(!cur1->right && cur2->right) cur1->right = cur2->right;
}
return root1;
}
哇哦!居然刷完了二叉树,接下来就是二叉搜索树啦!终不负所望掌握了递归呢!驾驭这种小妖精还是要多练呀,实践出真知!很短的一篇(其实也不短啦)献给在看的各位!嘿嘿,接下来就是二叉搜索树啦!