【数据结构】第六章 树
树的递归定义
树的基本术语
二叉树的定义
二叉树的常用性质
二叉树的前序遍历
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> res;
void dfs(TreeNode* root)
{
if(!root) return;
res.push_back(root->val);
if(root->left) dfs(root->left);
if(root->right) dfs(root->right);
}
vector<int> preorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
};
迭代写法:
思路:设置一个栈,保存将要访问的树根。开始时,将要访问的是整棵树,因此把二叉树的根节点存入栈中。然后重复以下过程:
从栈中取出一个节点,这个节点就是树的根节点;按照前序遍历,应该先遍历根节点,于是输出根节点的值;然后应该先前序遍历左子树,再前序遍历右子树,把这两个任务放入栈中;先放右子树,再放左子树。当栈为空时,说明所有的子树都已被遍历,函数结束。
/**
* 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;
stack<TreeNode*> stk;
while(root || stk.size())
{
while(root)
{
res.push_back(root->val);
stk.push(root);
root = root->left;
}
root = stk.top()->right;
stk.pop();
}
return res;
}
};
迭代写法二:把while里的while换成if else
/**
* 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;
stack<TreeNode*> stk;
while(root || stk.size())
{
if(root)
{
res.push_back(root->val);
stk.push(root);
root = root->left;
}
else
{
root = stk.top()->right;
stk.pop();
}
}
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> res;
void dfs(TreeNode* root)
{
if(!root) return;
if(root->left) dfs(root->left);
res.push_back(root->val);
if(root->right) dfs(root->right);
}
vector<int> inorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
};
非递归写法:
中序遍历和前序遍历一样,还是要采用一个栈存放要遍历的树的树根。在前序遍历中,根节点最先访问,因此根节点出栈后就可以被访问了。但中序遍历中,先要遍历左子树,接下去才能访问根节点,因此,当根节点出栈时还不能访问它,而要访问它的左子树,等左子树访问结束后才能访问它,此时要把根节点暂存一下。存哪里?由于左子树访问完后还要访问根节点,因此仍可以把它存在栈中,接着左子树也进栈。此时执行出栈操作,出栈的是左子树。左子树访问结束后,再次出栈的是根节点,此时根节点可被访问。根节点访问后,访问右子树,则将右子树进栈。
/**
* 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;
stack<TreeNode*> stk;
while(root || stk.size())
{
while(root)
{
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
res.push_back(root->val);
root = root->right;
}
return res;
}
};
迭代写法二:把while内的while换成if与else
/**
* 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;
stack<TreeNode*> stk;
while(root || stk.size())
{
if(root)
{
stk.push(root);
root = root->left;
}
else
{
root = stk.top();
stk.pop();
res.push_back(root->val);
root = root->right;
}
}
return res;
}
};
二叉树的后序遍历
递归写法:
/**
* 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> res;
void dfs(TreeNode* root)
{
if(!root) return;
if(root->left) dfs(root->left);
if(root->right) dfs(root->right);
res.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
};
迭代写法一(acwing):
巧解:我们在前面已经学过了前序遍历的迭代写法。而前序遍历的遍历顺序是“根左右”,我们可以对前序遍历进行修改,使其按照“根右左”的顺序遍历,然后把得到的结果翻转一下得到的“左右根”序列就是后序遍历的结果。
/**
* 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;
stack<TreeNode*> stk;
while(root || stk.size())
{
while(root)
{
res.push_back(root->val);
stk.push(root);
root = root->right; //这里是从前序遍历的root=root->left修改为right
}
root = stk.top()->left; // 从前序遍历的right修改为left
stk.pop();
}
reverse(res.begin(), res.end()); //翻转得到的“根右左”序列得到“左右根”序列
return res;
}
};
迭代写法二(巧解):参考题解
在栈中加入一个空节点作为标记,当弹出空节点时说明左右子树已经遍历完了,此时访问当前节点。如果弹出的节点不空,那就按照右子树、左子树的顺序入栈(因为这样最后出来的顺序才是左右根)
/**
* 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;
stack<TreeNode*> stk;
if (root) stk.push(root); // root非空,则入栈
while (stk.size())
{
auto t = stk.top(); // 先取出栈顶元素
stk.pop();
// 如果非空,说明当前结点没有被访问过(访问过的节点其上面有空节点先被弹出来)
if (t)
{
stk.push(t); // 那么我们把当前节点入栈
stk.push(NULL); // 然后再加一个空节点作为标记,表示已经访问过了
// 先放右子树,再放左子树,这样出栈的时候就是左右根(根节点最先入栈最后出)
if (t->right) stk.push(t->right);
if (t->left) stk.push(t->left);
}
else // 是空节点,那么说明其下一个节点就是答案数组中的元素
{
t = stk.top(); // 取出答案元素
stk.pop(); // 弹栈
res.push_back(t->val); // 将答案元素的值加入result数组
}
}
return res;
}
};
迭代写法三(考研复习资料书):
正常写法
/**
* 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;
stack<TreeNode*> stk;
while(root || stk.size()) // while 跳过初始时x==NULL的情况
{
if(root->left)
{
stk.push(root);
root = root->left;
}
else if(root->right)
{
stk.push(root);
root = root->right;
}
else // 当前结点没有孩子
{
res.push_back(root->val); // 访问当前结点
while(stk.size())
{
auto t = stk.top(); // 当前栈顶结点必然是当前结点的父结点
if(t->left == root && t->right) // 如果当前结点是左孩子,且父结点有右孩子
{
root = t->right; // 则转向右孩子,不pop父结点
break;
}
else
{
stk.pop(); // 否则pop并访问该父结点(此时孩子已经全部访问完)
root = t; // 循环直到栈空或父结点有右孩子
res.push_back(root->val);
}
}
}
if(stk.empty()) break; // 如果栈空了,说明根节点已被访问,遍历完成
}
return res;
}
};
二叉树的层序遍历
/**
* 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>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> q;
if(root) q.push(root);
while(q.size())
{
vector<int> level; // 存每层的节点
int len = q.size(); // len为当前层节点数
while(len -- )
{
auto t = q.front();
q.pop();
level.push_back(t->val);
if(t->left) q.push(t->left);
if(t->right) q.push(t->right);
}
res.push_back(level);
}
return res;
}
};
从前序和中序遍历构造二叉树
/**
* 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<int, int> pos;
TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir)
{
if(pl > pr) return NULL;
auto root = new TreeNode(preorder[pl]);
int k = pos[root->val];
root->left = build(preorder, inorder, pl + 1, pl + 1 + k - 1 - il, il, k - 1);
root->right = build(preorder, inorder, pl + 1 + k - 1 - il + 1, pr, k + 1, ir);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
for(int i = 0; i < inorder.size(); i ++ ) pos[inorder[i]] = i;
return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 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:
unordered_map<int, int> pos;
TreeNode* build(vector<int>& inorder, vector<int>& postorder, int il, int ir, int pl, int pr)
{
if(pl > pr) return NULL;
auto root = new TreeNode(postorder[pr]);
int k = pos[root->val];
root->left = build(inorder, postorder, il, k - 1, pl, pl + k - 1 - il);
root->right = build(inorder, postorder, k + 1, ir, pl + k - 1 - il + 1, pr - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
for(int i = 0; i < inorder.size(); i ++ ) pos[inorder[i]] = i;
return build(inorder, postorder, 0, inorder.size() - 1, 0, postorder.size() - 1);
}
};
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程