二叉树刷题篇

Posted 归宅观察部

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树刷题篇相关的知识,希望对你有一定的参考价值。

404.左叶子之和

计算给定二叉树的所有左叶子之和。示例: 

  3 

 /   

9  20   

    /   

  15   7 

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

https://leetcode-cn.com/problems/sum-of-left-leaves/
解决这道题的核心在于,如何判断左叶子?参考答案(x) 中给出的方法是,如果一个节点的左节点不为空,且左节点的左节点和左节点的右节点皆为空,那么这个节点的左节点是左叶子,即 通过一个节点的父节点和自身去判断是否为左节点
不过,在列出这个代码前,我们还可以有另一种思路,即省去节点、父节点这些复杂的判断条件,直接为一个节点添加一个“属性”,这个节点是左节点还是右节点,即使用一个变量,向左时为0,向右时为1,内核与父节点判断是一样的,贴出代码会更清楚 二叉树刷题篇(8)
class Solution {public: void traversal(TreeNode* cur,int direction,int &sum){ if(direction==0){ if(cur==NULL){ return; } if(cur->left==NULL&&cur->right==NULL){ sum+=cur->val; return; } traversal(cur->left,0,sum); traversal(cur->right,1,sum); } if(direction==1){ if(cur==NULL){ return; } traversal(cur->left,0,sum); traversal(cur->right,1,sum); } } int sumOfLeftLeaves(TreeNode* root) { int direction; int sum=0; if(root==NULL){ return 0; } TreeNode* cur=root; traversal(root->left,0,sum); traversal(root->right,1,sum); return sum; }};
这样就比较简便了。当然,原本的代码也放在这里参考下:
递归法:
class Solution {public: int sumOfLeftLeaves(TreeNode* root) { if (root == NULL) return 0;
int leftValue = sumOfLeftLeaves(root->left); // 左 int rightValue = sumOfLeftLeaves(root->right); // 右 // 中 int midValue = 0; if (root->left && !root->left->left && !root->left->right) { // 中 midValue = root->left->val; } int sum = midValue + leftValue + rightValue; return sum; }};//代码来自公众号代码随想录 作者为Carl
迭代法:
class Solution {public: int sumOfLeftLeaves(TreeNode* root) { stack<TreeNode*> st; if (root == NULL) return 0; st.push(root); int result = 0; while (!st.empty()) { TreeNode* node = st.top(); st.pop(); if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) { result += node->left->val; } if (node->right) st.push(node->right); if (node->left) st.push(node->left); } return result; }};
可以看出,迭代法实在是有些麻烦。虽然利用队列来层序遍历二叉树已经轻车熟路,但还是用递归去写更加方便二叉树刷题篇(8)


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

根据一棵树的中序遍历与后序遍历构造二叉树。注意: 你可以假设树中没有重复的元素。 

例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树: 

   3 

 /    

9    20 

      /    

    15    7

https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
显然,遍历已经无法阻止我们了 二叉树刷题篇(8),而且做了这么多道题都要做吐了,赶紧来一道构造二叉树来尝尝鲜 二叉树刷题篇(8)
还是先捋一下思路,二叉树的指针是单向的,因此中序、后序遍历给出的节点必须要先存入一个结构中,等到它的父节点出现然后再将指针指到这个节点。(balabala……)
好吧,自己想实在是不太好想 ,正确的思路是 用中序数组的最后一个数字去切分后序数组,因为中序数组的最后一个数字必然为树的根节点(或中间节点),然后利用递归的思想,将数组不断切分到最后即可得到我们需要的树。还是直接上代码。
class Solution {public: TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) { if(postorder.size()==0) return NULL; int rootValue=postorder[postorder.size()-1]; TreeNode* root=new TreeNode(rootValue); if(postorder.size()==1) return root; int delimiterIndex; for(delimiterIndex=0;delimiterIndex<postorder.size();delimiterIndex++){ if(inorder[delimiterIndex]==rootValue) break; }/* vector<int> leftInorder,rightInorder; * //注意这里的参数二为inorder.begin() + delimiterIndex。不需要再加1。 * leftInorder=inorder(inorder.begin(),inorder.begin()+delimiterIndex); * rightInorder=inorder(inorder.begin()+delimiterIndex+1,inorder.end()); * vector<int> leftPostorder,rightPostorder;  *leftPostorder=postorder(postorder.begin(),postorder.begin()+leftInorder.size()); *rightPostorder=postorder(postorder.begin()+leftInorder.size(),postorder.end()-1); */ vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex); vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() ); vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size()); vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end()-1); root->left=traversal(leftInorder,leftPostorder); root->right=traversal(rightInorder,rightPostorder); return root; } TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) { if (inorder.size() == 0 || postorder.size() == 0) return NULL; return traversal(inorder, postorder); }};
得到如上代码,这里注释掉的部分是为了提醒一下自己以后不要犯这么蠢的错误,居然用=运算符和迭代器去给向量赋值……
这里会发现代码的性能并不好,看卡尔大神的代码是如何优化的:
class Solution {private: // 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd) TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) { if (postorderBegin == postorderEnd) return NULL;
int rootValue = postorder[postorderEnd - 1]; TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int delimiterIndex; for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) { if (inorder[delimiterIndex] == rootValue) break; } // 切割中序数组 // 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd) int leftInorderBegin = inorderBegin; int leftInorderEnd = delimiterIndex; // 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd) int rightInorderBegin = delimiterIndex + 1; int rightInorderEnd = inorderEnd;
// 切割后序数组 // 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd) int leftPostorderBegin = postorderBegin; int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size // 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd) int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin); int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd); root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
return root; }public: TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) { if (inorder.size() == 0 || postorder.size() == 0) return NULL; // 左闭右开的原则 return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size()); }};
相当于传入切割位置,省去了重新定义数组的问题。这样就快得多了。
本日刷题结束,明天是除夕,祝打工人们除夕快乐~


以上是关于二叉树刷题篇的主要内容,如果未能解决你的问题,请参考以下文章

二叉树刷题篇(10) 二叉搜索树

二叉树刷题篇镜像二叉树与二叉树深度

二叉树刷题篇

二叉树刷题篇 最大二叉树 and 合并二叉树

二叉树刷题篇 完全二叉树的节点个数

二叉树刷题篇平衡二叉树与二叉树的所有路径