算法刷题 Leetcode 二叉树 0118-0119
各种遍历及解题模板
递归写法没什么好说的,基本功,砍瓜切菜。重点看非递归方式
144. 二叉树的前序遍历
- 递归方式
class Solution {
private:
vector<int> res;
public:
void LNR(TreeNode* root){
if(root==nullptr) return;
res.push_back(root->val);
LNR(root->left);
LNR(root->right);
}
vector<int> preorderTraversal(TreeNode* root) {
LNR(root);
return res;
}
};
- 迭代方式
vector<int> res;
stack<TreeNode*> st;
st.push(root);
TreeNode *cur = root;
while(cur!=nullptr && !st.empty()){
cur = st.top();
res.push_back(cur->val);
st.pop();
if(cur->right != nullptr) st.push(cur->right);
if(cur->left != nullptr) st.push(cur->left);
}
return res;
- 前序遍历模板
stack<TreeNode*> st;
st.push(root);
TreeNode *cur = root;
if(cur == nullptr) ..为空特判
while(!st.empty()){
cur = st.top(); st.pop();
。。操作
if(cur->right != nullptr) st.push(cur->right);
if(cur->left != nullptr) st.push(cur->left);
}
145. 二叉树的后序遍历
class Solution {
private:
vector<int> res;
public:
void LNR(TreeNode* root){
if(root==nullptr) return;
LNR(root->left);
LNR(root->right);
res.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
LNR(root);
return res;
}
};
- 迭代方式
曲线救国,后序遍历顺序是左右根,倒过来是根右左,而我们已经在实现前序遍历时做到了根左右遍历,只需调换遍历两个孩子的顺序即可,所以可以先根右左遍历,再整个翻转。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
st.push(root);
TreeNode *cur = root;
// 中右左
while(cur!=nullptr && !st.empty()){
cur = st.top();
res.push_back(cur->val);
st.pop();
if(cur->left != nullptr) st.push(cur->left); // 先push后访问
if(cur->right != nullptr) st.push(cur->right);
}
// 翻转为左右中
reverse(res.begin(), res.end());
return res;
}
};
94. 二叉树的中序遍历
- 递归方式
class Solution {
private:
vector<int> res;
public:
void LNR(TreeNode* root){
if(root==nullptr) return;
LNR(root->left);
res.push_back(root->val);
LNR(root->right);
}
vector<int> inorderTraversal(TreeNode* root) {
LNR(root);
return res;
}
};
-
迭代方式
同DFS迭代方式,先一直左下,无法左下了输出结点值,再进行右下
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
TreeNode * cur=root;
while(cur!=nullptr || !st.empty()){
if(cur!=nullptr){
st.push(cur);
cur = cur->left;
}else{
cur = st.top();
st.pop();
res.push_back(cur->val);
cur =cur->right;
}
}
return res;
}
};
- 迭代方式模板
stack<TreeNode*> st;
TreeNode * cur=root;
while(cur!=nullptr || !st.empty()){
if(cur!=nullptr){
st.push(cur);
cur = cur->left;
}else{
cur = st.top();
st.pop();
// 操作
cur =cur->right;
}
}
102. 二叉树的层序遍历
- 层序遍历模板
TreeNode *cur;
queue<TreeNode*> q;
if(root!=nullptr) q.push(root);
while( !q.empty() ){
int size = q.size();
vector<int> layer;
while(size--)
{
cur = q.front();
q.pop();
.. 进行操作
if(cur->left != nullptr) q.push(cur->left);
if(cur->right != nullptr) q.push(cur->right);
}
一层结束,进行操作
}
- 迭代法
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
TreeNode *cur;
queue<TreeNode*> q;
if(root!=nullptr) q.push(root);
vector<vector<int>> ans;
while( !q.empty()){
// 按层保存结果巧妙解法,先记录下当前层元素个数,再进行处理相应个数的元素即可进入下一层
int cnt = q.size();
vector<int> layer;
while(cnt--)
{
cur = q.front();
layer.push_back(cur->val);
q.pop();
if(cur->left != nullptr) q.push(cur->left);
if(cur->right != nullptr) q.push(cur->right);
}
ans.push_back(layer);
}
return ans;
}
};
589. N 叉树的前序遍历
- 迭代
class Solution {
public:
vector<int> preorder(Node* root) {
vector<int> res;
stack<Node*> st;
if(root != nullptr)st.push(root);
Node* cur;
while(!st.empty()){
cur = st.top();
st.pop();
res.push_back(cur->val); // 中
vector<Node*> children = cur->children;
// 从后往前push(右左),栈弹出来是从左往右(左右),形成左中右遍历
for(int i= children.size()-1; i>=0; i--){
st.push(children[i]);
}
}
return res;
}
};
- 递归
class Solution {
private:
vector<int> res;
public:
void visit(Node* root){
if(root == nullptr) return;
res.push_back(root-> val);
for(auto child : root->children){
// 无须在这里push
visit(child);
}
}
vector<int> preorder(Node* root) {
visit(root);
return res;
}
};
590. N 叉树的后序遍历
- 迭代方式
类似于二叉树的后序遍历迭代方式。官方题解的迭代方式还用到了哈希表,等二刷时再研究了。
class Solution {
public:
vector<int> postorder(Node* root) {
vector<int> res;
stack<Node*> st;
if(root != nullptr)st.push(root);
Node* cur;
while(!st.empty()){
cur = st.top();
st.pop();
res.push_back(cur->val); // 中
vector<Node*> children = cur->children;
for(auto child : children){ // (根)左右
st.push(child);
}
}
reverse(res.begin(), res.end());
return res;
}
};
-
递归方式
就将前序递归稍作修改即可。
class Solution {
private:
vector<int> res;
public:
void visit(Node* root){
if(root == nullptr) return;
for(auto child : root->children){
// 无须在这里push
visit(child);
}
res.push_back(root-> val);
}
vector<int> preorder(Node* root) {
visit(root);
return res;
}
};
操作二叉树
总结在前
树的操作题型归类:
- 各种遍历的应用
- 翻转二叉树
- 判树型
- 是否为对称二叉树:101题
- 是否为平衡二叉树:110题
- 是否为二叉排序树(二叉搜索树):98题
- …
- 树的属性
- 最大深度:104、最小深度:111、节点个数:222
- 路径相关:257、112
- 其他:404、513、
- 建树
- 106/654
跟树有关的题大都是建立在遍历的基础上,也就是说,我们可以基于遍历模板根据题目要求进行解答。
226. 翻转二叉树
- 递归法
class Solution {
public:
void reverseTree(TreeNode* root){
if(root==nullptr || root->left==nullptr && root->right == nullptr) return;
reverseTree(root->left);
reverseTree(root->right);
TreeNode *tmp = root->left;
root->left = root->right;
root->right = tmp;
}
TreeNode* invertTree(TreeNode* root) {
reverseTree(root);
return root;
}
};
- 迭代法:二刷补
101. 对称二叉树
- 迭代法
class Solution {
public:
bool check(TreeNode* left, TreeNode* right){
queue<TreeNode*> q; // 按层进行比较,用队列。 层序、前中后序都可,层序最简单
q.push(left); q.push(right);
while(!q.empty()){
left = q.front(); q.pop();
right = q.front(); q.pop();
if( left == nullptr && right == nullptr ) continue; // 排除都为空的情况
else if( left==nullptr || right==nullptr || left->val != right->val) return false; // 有一个为空或值不相等,返回false
q.push(left->left);
q.push(right->right);
q.push(left->right);
q.push(right->left);
}
return true;
}
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
};
- 递归法
class Solution {
public:
bool check(TreeNode* left, TreeNode* right){
if( left == nullptr && right == nullptr ) return true;
else if( left == nullptr || right == nullptr ) return false;
else if( left->val != right->val ) return false;
return check(left->left, right->right) && check(left->right, right->left);
}
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
};
104. 二叉树的最大深度
基于层序遍历,遍历完树即可得到结果。
- 迭代法
class Solution {
public:
int maxDepth(TreeNode* root) {
int deepth=0;
queue<TreeNode*> q;
if(root != nullptr) q.push(root);
else return 0;
TreeNode *p ;
while(!q.empty()){
int size = q.size();
while(size--){
p = q.front();
q.pop();
if( p->left != nullptr ) q.push(p->left);
if( p->right != nullptr ) q.push(p->right);
}
deepth++;
}
return deepth;
}
};
111. 二叉树的最小深度
仍基于层序遍历,找到叶子结点返回结果即可。
- 迭代法
class Solution {
public:
int minDepth(TreeNode* root) {
int deepth=0;
queue<TreeNode*> q;
if(root != nullptr) q.push(root);
else return 0;
TreeNode *p ;
while(!q.empty()){
int size = q.size();
while(size--){
p = q.front();
q.pop();
if( p->left == nullptr && p->right == nullptr ) return deepth+1;
if( p->left != nullptr ) q.push(p->left);
if( p->right != nullptr ) q.push(p->right);
}
deepth++;
}
return deepth;
}
};
222. 完全二叉树的节点个数
- 迭代版:不利用完全二叉树性质,二叉树统计个数通用版
int countNodes(TreeNode* root) {
TreeNode *cur;
queue<TreeNode*> q;
if(root!=nullptr) q.push(root);
int ans = 0;
while( !q.empty() ){
cur = q.front();
q.pop();
ans++;
if(cur->left != nullptr) q.push(cur->left);
if(cur->right != nullptr) q.push(cur->right);
}
return ans;
}
- 递归版:利用完全二叉树性质
完全二叉树的特性:1、要么为满二叉树,要么只有最后一层未满,及最后一层的结点个数为 [ 1, 2^(h-1) ];2、叶子结点在最后两层,
官方题解让人不想看,carl 的解法容易理解得多,有点平衡二叉树的左右子树都是平衡二叉树那味了。
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == nullptr) return 0;
TreeNode *p = root->left;
int leftdeepth = 0, rightdeepth = 0;
while(p != nullptr) {
leftdeepth++;
p = p -> left;
}
p = root->right;
while(p != nullptr) {
rightdeepth++;
p = p->right;
}
if(leftdeepth == rightdeepth) return (2<<leftdeepth) - 1;
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
110. 平衡二叉树
刚说到平衡二叉树,下一题就来了。不想会。。
257. 二叉树的所有路径
- 递归法:前序遍历递归模型
class Solution {
private:
vector<string> res;
public:
void dfs(TreeNode* root, string s){ // 遍历模型:根左右
if(root == nullptr) return;
if(root->left == nullptr && root->right == nullptr){ // 叶子结点
s += to_string(root->val);
res.push_back(s);
return;
}
dfs(root->left, s + to_string(root->val) + "->"); // 省得回溯
dfs(root->right, s + to_string(root->val) + "->");
}
vector<string> binaryTreePaths(TreeNode* root) {
dfs(root, "");
return res;
}
};
404. 左叶子之和
遍历框架:左中右 中序遍历
- 迭代法
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
// 左中右 中序遍历框架
int sum = 0;
stack<TreeNode*> st;
TreeNode * cur=root;
if(cur->left == nullptr && cur->right == nullptr) return 0;
while(cur!=nullptr || !st.empty()){
if(cur!=nullptr){
st.push(cur);
cur = cur->left;
}else{
cur = st.top();
st.pop();
if(cur->left == nullptr && cur->right == nullptr) sum += cur->val;
cur =cur->right;
if(cur != nullptr && cur->left == nullptr && cur->right == nullptr)
cur = nullptr; // 右叶子,这里赋空下一循环就重新取结点,continue无用
}
}
return sum;
}
};
513. 找树左下角的值
用层序模板最方便,理论上讲中序未必不行,但是这个深度我算是放弃debug了,二刷整活。
- 迭代版
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
TreeNode *cur;
queue<TreeNode*> q;
if(root!=nullptr) q.push(root);
int ans;
while( !q.empty() ){
int size = q.size();
vector<int> layer;
for(int i=0; i<size; i++)
{
cur = q.front();
q.pop();
if(i==0) ans = cur->val;
if(cur->left != nullptr) q.push(cur->left);
if(cur->right != nullptr) q.push(cur->right);
}
}
return ans;
}
};
112. 路径总和
- 前序遍历模板:递归版
class Solution {
private:
bool res=false;
public:
void LNR(TreeNode* root, int sum, int tar){
if( root == nullptr || res) return;
sum += root->val;
if( root->left == nullptr && root->right == nullptr){
if(sum == tar ) res = true;
return;
}
LNR(root->left, sum, tar);
LNR(root->right, sum, tar);
sum -= root->val; // 回溯
}
bool hasPathSum(TreeNode* root, int targetSum) {
LNR(root, 0, targetSum);
return res;
}
};
106. 从中序与后序遍历序列构造二叉树
这题是建树了,不是基于树进行操作,遍历模板不能派上用场。
- 递归法
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(postorder.size() == 0) return nullptr;
// 后序遍历中取出根
int rootval = postorder[postorder.size()-1];
TreeNode *root = new TreeNode(rootval);
// 中序遍历中找到根
for(int i=0; i<inorder.size(); i++){
if(inorder[i] == rootval){ // 根结点位置
// 建左子树
vector<int> leftinorder(inorder.begin(), inorder.begin() + i); // 左闭右开截取
vector<int> leftpostorder(postorder.begin(), postorder.begin() + i);
root ->left = buildTree(leftinorder, leftpostorder);
// 建右子树
vector<int> rightinorder(inorder.begin() + i + 1, inorder.end());
postorder.resize(postorder.size()-1); // 根结点不包括
vector<int> rightpostorder(postorder.begin()+leftinorder.size(), postorder.end());
root ->right = buildTree(rightinorder, rightpostorder);
}
}
return root;
}
};
654. 最大二叉树
这是一道建树变种,基于上一题代码改改即可。
- 递归
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& a) {
if (a.size() == 0) return nullptr;
int maxIndex = 0, max = 0;
for(int i=0; i<a.size(); i++){
if(a[i]>=max){
maxIndex = i;
max = a[i];
}
}
// 建根
TreeNode *root = new TreeNode(max);
// 建左子树
vector<int> left(a.begin(), a.begin() + maxIndex); // 左闭右开截取
root ->left = constructMaximumBinaryTree(left);
// 建右子树
vector<int> right(a.begin() + maxIndex + 1, a.end());
root ->right = constructMaximumBinaryTree(right);
return root;
}
};
617. 合并二叉树
还是用建树模板即可。
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1 == nullptr && root2 == nullptr) return nullptr;
else if(root1 == nullptr) return root2;
else if(root2 == nullptr) return root1;
// 都不为空
TreeNode *root = new TreeNode(root1->val + root2->val);
root->left = mergeTrees(root1->left, root2->left);
root->right = mergeTrees(root1->right, root2->right);
return root;
}
};
98. 验证二叉搜索树
中序遍历后判断序列是否升序,否则不是二叉搜索树。
class Solution {
public:
bool isValidBST(TreeNode* root) {
TreeNode* p = root;
stack<TreeNode*> st;
vector<int> res;
while(p != nullptr || !st.empty()){
if(p!=nullptr){
st.push(p);
p = p->left;
}
else{
p = st.top(); st.pop();
res.push_back(p->val);
p = p->right;
}
}
for(int i=1; i<res.size(); i++){
if(res[i] <= res[i-1]) {
return false;
}
}
return true;
}
};