【二十七】【C++】二叉树练习

606. 根据二叉树创建字符串

给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。

空节点使用一对空括号对 "()" 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

示例 1:

 
输入:root = [1,2,3,4]
输出:"1(2(4))(3)"
解释:初步转化后得到 "1(2(4)())(3()())" ,但省略所有不必要的空括号对后,字符串应该是"1(2(4))(3)" 。

示例 2:

 
输入:root = [1,2,3,null,4]
输出:"1(2()(4))(3)"
解释:和第一个示例类似,但是无法省略第一个空括号对,否则会破坏输入与输出一一映射的关系。

提示:

  • 树中节点的数目范围是 [1, 10(4)]

  • -1000 <= Node.val <= 1000

 
class Solution {
public:string tree2str(TreeNode *root) {if (root == nullptr) {return "";}if (root->left == nullptr && root->right == nullptr) {return to_string(root->val);}if (root->right == nullptr) {return to_string(root->val) + "(" + tree2str(root->left) + ")";}return to_string(root->val) + "(" + tree2str(root->left) + ")(" + tree2str(root->right) + ")";}
};

这道题目蕴含的规则,

  • 每个节点的值都转换成字符串。

  • 对于非叶子节点,其左子节点(如果存在)的字符串表示将被放置在一对括号内紧跟在该节点的值之后。如果左子节点不存在但右子节点存在,将使用一对空括号()表示左子节点的位置。

  • 对于非叶子节点,其右子节点(如果存在)的字符串表示将被放置在一对括号内,紧跟在左子节点的字符串表示之后。

空节点:如果当前节点是nullptr,返回空字符串""

叶子节点:如果当前节点是叶子节点(即没有左子节点也没有右子节点),仅返回当前节点值的字符串表示。

只有左子节点:如果当前节点仅有左子节点(没有右子节点),则返回当前节点值的字符串表示,后面跟着左子节点的字符串表示,左子节点的字符串表示被一对括号包围。

既有左子节点也有右子节点:如果当前节点既有左子节点也有右子节点,返回当前节点值的字符串表示,后面依次跟着左子节点和右子节点的字符串表示,这两个子节点的字符串表示各自被一对括号包围。

102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

 
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

 
输入:root = [1]
输出:[[1]]

示例 3:

 
输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000]

  • -1000 <= Node.val <= 1000

 
class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {vector <vector <int>> ret;if (!root) {return ret;}queue <TreeNode*> q;q.push(root);while (!q.empty()) {int currentLevelSize = q.size();ret.push_back(vector <int> ());for (int i = 1; i <= currentLevelSize; ++i) {auto node = q.front(); q.pop();ret.back().push_back(node->val);if (node->left) q.push(node->left);if (node->right) q.push(node->right);}}return ret;}
};

如果传入的根节点root为空,则函数立即返回一个空的二维向量ret

初始化一个队列q,用于存储待访问的节点。首先将根节点root入队。

当队列不为空时,进行循环。循环的每一轮对应树的一层。

在循环开始时,记录当前队列的大小currentLevelSize,这个大小代表了当前层的节点数。

添加一个新的空向量到ret中,用于存储当前层的节点值。

通过一个for循环遍历当前层的所有节点。循环次数由currentLevelSize决定。

每次循环中,从队列中取出队首元素(node),这代表当前层的一个节点。

node的值加入到ret的最后一个向量中,这个向量代表当前层的节点值集合。

如果node的左子节点存在,则将左子节点入队;如果node的右子节点存在,则将右子节点入队。这确保了下一层的节点将会在后续的迭代中被处理。

107. 二叉树的层序遍历 II

给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

示例 1:

 
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]

示例 2:

 
输入:root = [1]
输出:[[1]]

示例 3:

 
输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000]

  • -1000 <= Node.val <= 1000

 
class Solution {
public:vector<vector<int>> levelOrderBottom(TreeNode* root) {vector<vector<int>> ret;if (!root) {return ret;}queue<TreeNode*> q;q.push(root);while (!q.empty()) {int currentLevelSize = q.size();ret.push_back(vector<int>());for (int i = 1; i <= currentLevelSize; i++) {auto node = q.front();q.pop();ret.back().push_back(node->val);if (node->left)q.push(node->left);if (node->right)q.push(node->right);}}reverse(ret.begin(), ret.end());return ret;}
};

如果传入的根节点root为空,则函数立即返回一个空的二维向量ret

初始化一个队列q,用于存储待访问的节点。首先将根节点root入队。

当队列不为空时,进行循环。循环的每一轮对应树的一层。

在循环开始时,记录当前队列的大小currentLevelSize,这个大小代表了当前层的节点数。

添加一个新的空向量到ret中,用于存储当前层的节点值。

通过一个for循环遍历当前层的所有节点。循环次数由currentLevelSize决定。

每次循环中,从队列中取出队首元素(node),这代表当前层的一个节点。

node的值加入到ret的最后一个向量中,这个向量代表当前层的节点值集合。

如果node的左子节点存在,则将左子节点入队;如果node的右子节点存在,则将右子节点入队。这确保了下一层的节点将会在后续的迭代中被处理。

在完成所有层的遍历后,使用reverse函数将ret中的向量顺序反转,以达到自底向上的层序遍历效果。

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

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

 
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

 
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

提示:

  • 树中节点数目在范围 [2, 10(5)] 内。

  • -10(9) <= Node.val <= 10(9)

  • 所有 Node.val 互不相同

  • p != q

  • pq 均存在于给定的二叉树中。

 
class Solution {
public:TreeNode* ans;bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {if (root == nullptr)return false;bool lson = dfs(root->left, p, q);bool rson = dfs(root->right, p, q);if ((lson && rson) ||((root->val == p->val || root->val == q->val) && (lson || rson))) {ans = root;}return lson || rson || (root->val == p->val || root->val == q->val);}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {dfs(root, p, q);return ans;}
};

dfs 函数:

遍历树以检查当前节点root是否是pq的祖先,或者是否自身是pq

当前考察的节点root,以及要找的节点pq

如果当前root或其任何子节点是pq,则返回true,否则返回false

逻辑:

如果当前节点是nullptr,返回false

检查当前节点的左子树(lson)和右子树(rson)是否包含pq

如果下面任一条件为真,则当前节点rootpq的最低公共祖先:

lsonrson都为真,意味着pq分别位于当前节点的两侧。

当前节点是pq之一,并且pq中的另一个位于当前节点的某个子树中。

更新ans为当前的最低公共祖先节点。

lowestCommonAncestor 函数:

找到并返回给定二叉树中两个节点pq的最低公共祖先。

调用dfs函数遍历树,dfs过程中会更新anspq的最低公共祖先。完成遍历后,ans即为所求的最低公共祖先节点。

二叉搜索树与双向链表_牛客题霸_牛客网

描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示

数据范围:输入二叉树的节点数 0≤n≤1000,二叉树中每个节点的值 0≤val≤1000 要求:空间复杂度O(1)(即在原树上操作),时间复杂度O(n)

注意:

1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继 2.返回链表中的第一个节点的指针 3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构

4.你不用输出双向链表,程序会根据你的返回值自动打印输出

输入描述:

二叉树的根节点

返回值描述:

双向链表的其中一个头节点。

示例1

输入:

{10,6,14,4,8,12,16}

返回值:

From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;

说明:

输入题面图中二叉树,输出的时候将双向链表的头节点返回即可。

示例2

输入:

{5,4,#,3,#,2,#,1}

返回值:

From left to right are:1,2,3,4,5;From right to left are:5,4,3,2,1;

说明:

5 / 4 / 3 / 2 / 1 树的形状如上图

法一:中序+链接

 
/*
struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};*/
class Solution {
public:vector<TreeNode*> TreeList;//定义一个数组,根据中序遍历来存储结点。void inorder(TreeNode* root){if (!root) return;inorder(root->left);TreeList.push_back(root);inorder(root->right);}TreeNode* Convert(TreeNode* pRootOfTree) {if (!pRootOfTree) return pRootOfTree;inorder(pRootOfTree);for (int i=0;i<TreeList.size()-1;i++){ //根据数组中的顺序将结点连接,注意i的范围。TreeList[i]->right = TreeList[i+1];TreeList[i+1]->left = TreeList[i];}return TreeList[0];//数组的头部存储的是双向链表的第一个结点。}};

中序遍历 (inorder 函数)

inorder 函数对二叉树进行中序遍历。中序遍历的顺序是先遍历左子树,然后访问根节点,最后遍历右子树。

在遍历过程中,每访问到一个节点,就将其加入到TreeList向量中。由于是中序遍历,TreeList中的节点自然按照值的升序排列。

转换为双向链表 (Convert 函数)

Convert 函数首先检查给定的树(pRootOfTree)是否为空。如果为空,则直接返回空指针。

然后,调用inorder函数进行中序遍历,填充TreeList向量。

接下来,遍历TreeList向量,将每个节点按顺序连接起来形成双向链表。对于向量中的每个节点,除了最后一个节点外:

将当前节点的right指针指向下一个节点。

将下一个节点的left指针指向当前节点。

最后,返回TreeList中的第一个元素,即双向链表的头节点。

法二:线索化

 
/*
struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};*/
class Solution {
public:TreeNode* preNode;//preNode一定是全局变量。TreeNode* Convert(TreeNode* pRootOfTree) {if (!pRootOfTree) return pRootOfTree;TreeNode* p = pRootOfTree;while (p->left) p = p->left;//找到双向链表的开头。inorder(pRootOfTree);return p;}void inorder(TreeNode* root){if (!root) return;inorder(root->left);//当前结点中需要进校的调整。root->left = preNode;if (preNode){preNode->right = root;}preNode = root;//更新preNode,指向当前结点,作为下一个结点的前继。inorder(root->right);}
};

Convert 函数:

首先检查树是否为空。如果是空树,则直接返回空指针。

然后找到最左边的节点,这将是双向链表的头节点。这一步确保能返回转换后的双向链表的起始点。

调用inorder函数进行中序遍历并在遍历过程中逐步构造双向链表。

inorder 函数:

这个函数通过中序遍历递归地访问树的每个节点。

在访问每个节点时,它将当前节点的left指针指向preNode(即中序遍历中当前节点的前一个节点)。

如果preNode非空,它还将preNoderight指针指向当前节点,从而在两个节点之间建立双向连接。

然后更新preNode为当前节点,以便在下一次迭代中使用。

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

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1] 输出: [-1]

提示:

  • 1 <= preorder.length <= 3000

  • inorder.length == preorder.length

  • -3000 <= preorder[i], inorder[i] <= 3000

  • preorderinorder无重复 元素

  • inorder 均出现在 preorder

  • preorder 保证 为二叉树的前序遍历序列

  • inorder 保证 为二叉树的中序遍历序列

 
class Solution {
private:unordered_map<int, int> index;public:TreeNode* myBuildTree(const vector<int>& preorder,const vector<int>& inorder, int preorder_left,int preorder_right, int inorder_left,int inorder_right) {if (preorder_left > preorder_right) {return nullptr;}// 前序遍历中的第一个节点就是根节点int preorder_root = preorder_left;// 在中序遍历中定位根节点int inorder_root = index[preorder[preorder_root]];// 先把根节点建立出来TreeNode* root = new TreeNode(preorder[preorder_root]);// 得到左子树中的节点数目int size_left_subtree = inorder_root - inorder_left;// 递归地构造左子树,并连接到根节点// 先序遍历中「从 左边界+1 开始的// size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到// 根节点定位-1」的元素root->left = myBuildTree(preorder, inorder, preorder_left + 1,preorder_left + size_left_subtree,inorder_left, inorder_root - 1);// 递归地构造右子树,并连接到根节点// 先序遍历中「从 左边界+1+左子树节点数目 开始到// 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1,preorder_right, inorder_root + 1, inorder_right);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int n = preorder.size();// 构造哈希映射,帮助我们快速定位根节点for (int i = 0; i < n; ++i) {index[inorder[i]] = i;}return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);}
};

buildTree 函数:

这个函数是公有的,作为类外部调用的接口。它接受两个参数:分别代表二叉树前序遍历和中序遍历的数组。

首先,函数通过遍历中序遍历数组来填充一个哈希映射index,该映射用于快速查找中序遍历中任一值对应的索引,这样可以快速定位到根节点在中序遍历中的位置。

然后,调用私有的myBuildTree函数,传入前序遍历和中序遍历的数组及其对应的索引范围,开始递归构造树。

myBuildTree 函数:

这是一个递归函数,用于根据给定的前序遍历和中序遍历的一部分来构造子树,并返回子树的根节点。

首先,检查当前递归的子数组是否有效(即前序遍历的左索引是否大于右索引),如果无效,则返回nullptr

根据前序遍历的首元素确定根节点,然后在中序遍历中找到根节点的位置,从而确定左右子树的范围。

计算左子树的大小,然后递归构造左子树和右子树。

最后,将构造好的左右子树分别连接到根节点的左右指针上,并返回根节点。

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

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3] 输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1] 输出:[-1]

提示:

  • 1 <= inorder.length <= 3000

  • postorder.length == inorder.length

  • -3000 <= inorder[i], postorder[i] <= 3000

  • inorderpostorder 都由 不同 的值组成

  • postorder 中每一个值都在 inorder

  • inorder 保证是树的中序遍历

  • postorder 保证是树的后序遍历

 
class Solution {int post_idx;unordered_map<int, int> idx_map;public:TreeNode* helper(int in_left, int in_right, vector<int>& inorder,vector<int>& postorder) {// 如果这里没有节点构造二叉树了,就结束if (in_left > in_right) {return nullptr;}// 选择 post_idx 位置的元素作为当前子树根节点int root_val = postorder[post_idx];TreeNode* root = new TreeNode(root_val);// 根据 root 所在位置分成左右两棵子树int index = idx_map[root_val];// 下标减一post_idx--;// 构造右子树root->right = helper(index + 1, in_right, inorder, postorder);// 构造左子树root->left = helper(in_left, index - 1, inorder, postorder);return root;}TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {// 从后序遍历的最后一个元素开始post_idx = (int)postorder.size() - 1;// 建立(元素,下标)键值对的哈希表int idx = 0;for (auto& val : inorder) {idx_map[val] = idx++;}return helper(0, (int)inorder.size() - 1, inorder, postorder);}
};

buildTree 函数:

作为类的公共接口,这个函数接收两个数组:一个是树的中序遍历结果inorder,另一个是后序遍历结果postorder

初始化post_idx为后序遍历数组的最后一个元素的索引,这个元素是树的根节点。

使用inorder数组的元素建立一个哈希表idx_map,用于快速查找中序遍历中元素的索引。

调用helper函数来构建并返回树的根节点。

helper 函数:

递归函数,用于根据给定的中序遍历和后序遍历的某一部分来构造子树,并返回子树的根节点。

参数in_leftin_right定义了当前子树在中序遍历中的范围。

如果in_left大于in_right,说明没有节点可以用来构建子树,返回nullptr

使用postorder[post_idx]确定当前子树的根节点,并据此在中序遍历中找到根节点的索引index

递归构建右子树和左子树。注意,必须先构建右子树,因为在后序遍历中,根节点之前的元素是先遍历右子树再遍历左子树的。

144. 二叉树的前序遍历 - 力扣(LeetCode)

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

输入:root = [1,null,2,3] 输出:[1,2,3]

示例 2:

输入:root = [] 输出:[]

示例 3:

输入:root = [1] 输出:[1]

示例 4:

输入:root = [1,2] 输出:[1,2]

示例 5:

输入:root = [1,null,2] 输出:[1,2]

提示:

  • 树中节点数目在范围 [0, 100]

  • -100 <= Node.val <= 100

进阶:递归算法很简单,你可以通过迭代算法完成吗?

 
class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;if (root == nullptr) {return res;}stack<TreeNode*> stk;TreeNode* node = root;while (!stk.empty() || node != nullptr) {while (node != nullptr) {res.emplace_back(node->val);stk.emplace(node);node = node->left;}node = stk.top();stk.pop();node = node->right;}return res;}
};

前序遍历的顺序是先访问根节点,然后遍历左子树,最后遍历右子树。这里使用了迭代的方法而不是递归,主要依靠栈(stack)来实现。

定义一个名为preorderTraversal的函数,它接受一个指向TreeNode类型的指针root作为参数,表示二叉树的根节点。函数返回一个整数向量(vector<int>),包含树的前序遍历结果。

定义一个名为res的整数向量,用来存储遍历过程中节点的值,也就是前序序列的输出结果。

检查根节点是否为nullptr,如果是,表示树为空,直接返回空的res向量。

定义一个栈stk,用来存储节点的指针。这个栈帮助我们在遍历过程中回溯到之前的节点。栈内存放的节点表示已经访问过一次的节点,也就是第二次待访问的节点。

定义一个指针node,初始化为根节点root。这个指针将用来遍历树。

外循环继续执行,直到栈为空且node指针为nullptr。这意味着树已经被完全遍历。

 
            while (node != nullptr) {res.emplace_back(node->val);stk.emplace(node);node = node->left;}

这个内层循环遍历到当前子树的最左边的节点,沿途经过的每个节点都被处理并压入栈中。

将当前节点的值添加到res向量中。这符合前序遍历中“根节点-左子树-右子树”的顺序。第一次访问节点是访问该节点的根。存放到res向量中。

将当前节点压入栈中,以便后续能够回溯到其父节点。栈内的节点表示已经访问过一次的节点,也就是第二次访问待访问的节点。

node指针移动到当前节点的左子节点,继续前序遍历过程。

 
            node = stk.top();stk.pop();node = node->right;

当到达最左边的节点(nodenullptr)时,从栈中取出最近一个还未处理其右子树的节点。

node指针移动到刚刚从栈中取出的节点的右子节点,然后继续循环,进行右子树的遍历。

在树被完全遍历后,返回包含前序遍历结果的res向量。

94. 二叉树的中序遍历 - 力扣(LeetCode)

给定一个二叉树的根节点 root ,返回 它的 中序 遍历

示例 1:

输入:root = [1,null,2,3] 输出:[1,3,2]

示例 2:

输入:root = [] 输出:[]

示例 3:

输入:root = [1] 输出:[1]

提示:

  • 树中节点数目在范围 [0, 100]

  • -100 <= Node.val <= 100

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

 
class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;stack<TreeNode*> stk;while (root != nullptr || !stk.empty()) {while (root != nullptr) {stk.push(root);root = root->left;}root = stk.top();stk.pop();res.push_back(root->val);root = root->right;}return res;}
};

定义了一个名为inorderTraversal的函数,它接收一个指向TreeNode类型的指针root作为参数,表示二叉树的根节点。函数返回一个整数向量(vector<int>),包含树的中序遍历结果。

定义一个名为res的整数向量,用来存储遍历过程中节点的值。

定义一个栈stk,用于存储节点指针。这个栈帮助我们在遍历过程中回溯到之前的节点。

这个循环继续执行,直到root指针为空且栈为空。这意味着树已经被完全遍历。

这个内层循环遍历到当前子树的最左边的节点,并将沿途经过的每个节点压入栈中。

 
            while (root != nullptr) {stk.push(root);root = root->left;}

将当前节点压入栈中。这一步确保了我们可以在完成左子树的遍历后,回到当前节点。

root指针移动到当前节点的左子节点,继续中序遍历的过程。

 
            root = stk.top();stk.pop();res.push_back(root->val);root = root->right;

当到达最左边的节点时(即rootnullptr),从栈中取出最近一个还未访问其值的节点。

将从栈中取出的节点的值添加到res向量中。这一步是在访问节点的值,符合中序遍历的顺序。

root指针移动到刚刚访问过的节点的右子节点,然后继续循环,以遍历右子树。

在树被完全遍历后,返回包含中序遍历结果的res向量。

145. 二叉树的后序遍历 - 力扣(LeetCode)

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历

示例 1:

输入:root = [1,null,2,3] 输出:[3,2,1]

示例 2:

输入:root = [] 输出:[]

示例 3:

输入:root = [1] 输出:[1]

提示:

  • 树中节点的数目在范围 [0, 100]

  • -100 <= Node.val <= 100

进阶:递归算法很简单,你可以通过迭代算法完成吗?

 
class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> res;if (root == nullptr) {return res;}stack<TreeNode*> stk;TreeNode* prev = nullptr;while (root != nullptr || !stk.empty()) {while (root != nullptr) {stk.emplace(root);root = root->left;}root = stk.top();stk.pop();if (root->right == nullptr || root->right == prev) {res.emplace_back(root->val);prev = root;root = nullptr;} else {stk.emplace(root);root = root->right;}}return res;}
};

这行代码定义了一个名为postorderTraversal的函数,它接收一个指向TreeNode类型的指针root作为参数,表示二叉树的根节点。函数返回一个整数向量(vector<int>),包含树的后序遍历结果。

定义一个名为res的整数向量,用来存储遍历过程中节点的值

这段代码检查根节点是否为nullptr,如果是,表示树为空,直接返回空的res向量。

定义一个栈stk,用来存储节点的指针。这个栈帮助我们在遍历过程中回溯到之前的节点。

定义一个指针prev,初始化为nullptr。这个指针用来记录最近访问过的节点,以便判断是否可以访问当前节点的右子节点或是应该访问当前节点(在后序遍历中,一个节点在其右子节点被访问后才被访问)。

这个循环继续执行,直到root指针为nullptr且栈为空。这意味着树已经被完全遍历。

 
            while (root != nullptr) {stk.emplace(root);root = root->left;}

这个内层循环遍历到当前子树的最左边的节点,沿途经过的每个节点都被压入栈中。

将当前节点压入栈中,以便后续能够回溯到其父节点。

root指针移动到当前节点的左子节点,继续遍历过程。

 
            root = stk.top();stk.pop();

当到达最左边的节点(rootnullptr时),从栈中取出最近一个还未处理其右子树或自身的节点。

 
            if (root->right == nullptr || root->right == prev) {res.emplace_back(root->val);prev = root;root = nullptr;} else {stk.emplace(root);root = root->right;}

对于栈内的每一个节点,先访问左子树,第一次入栈表示的是已经访问过一次的节点,第二次访问就是出栈,判断是否可以直接输出,如果不能直接输出又将该节点入栈,但是我们入的是同一个栈,如何表示是该节点是第三次访问还是第二次访问,就看prev上一个输出的节点是什么,如果上一个输出的节点是右孩子,说明这个节点是第三次访问。此时直接输出即可。

这个条件判断当前节点的右子节点是否不存在或已经被访问。如果是,那么可以访问当前节点。

将当前节点的值添加到res向量中。这符合后序遍历中“左子树-右子树-根节点”的顺序。

更新prev指针为刚刚访问过的节点。

root指针设置为nullptr,这表示当前节点已处理完毕,下一次循环将从栈顶节点继续执行。

如果当前节点的右子节点存在且未被访问,则将当前节点再次压入栈中,并将root指针移动到右子节点,继续遍历过程。

在树被完全遍历后,返回包含后序遍历结果的res向量。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2806150.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

集成TinyMCE富文本编辑器

若依的基础上集成TinyMCE富文本编辑器 前端bootstrap TinyMCE官网链接 TinyMCE所需静态资源下载链接 开源项目-若依链接 将TinyMCE静态资源包放入项目中&#xff1b; 代码引入css&#xff1a; <!-- 引入TinyMCE CSS --><link th:href"{/ajax/libs/tinymce/j…

抖音视频评论数据提取软件|抖音数据抓取工具

一、开发背景&#xff1a; 在业务需求中&#xff0c;我们经常需要下载抖音视频。然而&#xff0c;在网上找到的视频通常只能通过逐个复制链接的方式进行抓取和下载&#xff0c;这种操作非常耗时。我们希望能够通过关键词自动批量抓取并选择性地下载抖音视频。因此&#xff0c;为…

什么是调制比

一般情况下&#xff0c;调制波和载波的最大幅值是不一样的。 正弦波的最大幅值低于三角波的最大幅值。 这样做的目的就是产生最大占空比&#xff08;2000W逆变器中最大占空比是80%&#xff09; 调制波就是正弦波的最大幅值比三角载波的最大幅值 问题1 为什么调制波要小于1&…

Oracle不能启动,提示设备空间不足,实际上是信号量不够(ORA-27300,ORA-27301,ORA-27302)

我的一个客户的Oracle数据库不能启动&#xff0c;出现下面的提示&#xff1a; ORA-27154: post/wait create failed ORA-27300: OS system dependent operation:semget failed with status: 28 ORA-27301: OS failure message: No space left on device ORA-27302: failure oc…

邮件发送/接收过程分析、常见邮箱sport/dport列举、检测规则开发思路分析

一、邮件发送和接收过程分析 &#xff08;转载自&#xff1a;邮件的发送和接收过程——STMP、POP、IMAP、MIME_当收件人接收电子邮件时自己的邮件服务器通过什么收文件-CSDN博客&#xff09; 电子邮件发送协议 是一种基于“ 推 ”的协议&#xff0c;主要包括 SMTP &#xff1…

代码随想录算法训练营第60天 | 647.回文子串 + 516.最长回文子序列 + 动态规划总结篇

今日任务 647. 回文子串 516.最长回文子序列 动态规划总结篇 647.回文子串 - Medium 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串…

1.openEuler概述及安装指南(一)

openEuler OECA认证辅导&#xff0c;标红的文字为学习重点和考点。 1.openEuler简介 openEuler是一款面向全球的开源操作系统 支持ARM、X86等多种处理器&#xff0c;能够充分释放计算芯片的潜能&#xff1a;高效、稳定、安全 适用于数据库、大数据、云计算、人工智能等多种应用…

maven3旧版本的下载地址(含新版本)

因为现有的3.8版本与IDEA不兼容&#xff0c;我需要下载3.6版本&#xff0c;但是官网的位置非常隐蔽&#xff0c;找了很多资料才看到。故记录一下。 第一步 进入网址&#xff0c;选择需要的版本 Index of /dist/maven/maven-3 第二步 选择binaries 第三步 选择zip文件下载就可…

Seata Server 服务搭建

概述 Seata 分布式事务需要 Seata Seaver 支持&#xff0c;Seata Server在 架构中扮演着 事务管理器的角色。Seata 服务需要往 Nacos 注册中心注册、以及读取配置文件&#xff0c;因此 Seata 启动前需要部署 Nacos 环境。 安装包下载 下载地址: https://download.csdn.net/dow…

【ArcGIS】利用高程进行坡度分析:区域面/河道坡度

在ArcGIS中利用高程进行坡度分析 坡度ArcGIS实操案例1&#xff1a;流域面上坡度计算案例2&#xff1a;河道坡度计算2.1 案例数据2.2 操作步骤 参考 坡度 坡度是地表单元陡缓的程度&#xff0c;通常把坡面的垂直高度和水平距离的比值称为坡度。 坡度的表示方法有百分比法、度数…

如何快速导出vercel project中的环境变量

我在vercel中集成了某些插件或者链接了数据库&#xff0c;要如何快速的导出这些环境变量呢&#xff1f; 具体方法如下&#xff1a; npm i -g vercelvercel linkvercel env pull .env.local首先是安装vercel然后登录vercel 最后拉取环境变量到.env.local

虚拟机的四种网络模式对比

nat网络地址转换 nat网络 桥接 内网模式 仅主机

海外媒体推广通过5个发稿平台开拓国际市场-华媒舍

随着全球化的进程&#xff0c;国际市场对于企业的吸引力日益增加。进入国际市场并获得成功并非易事。海外媒体推广发稿平台成为了一种重要的营销手段&#xff0c;能够帮助企业在国际市场中建立品牌形象、传递信息和吸引目标受众。本文介绍了五个海外媒体推广发稿平台&#xff0…

Vue3项目结构分析

node_modules: 是项目npm install下载的node依赖库。 public&#xff1a; favicon.ico: 网页图标logo图片。index.html: 入口html。是一个基础的html页面&#xff0c;其中进行网页最基础的设置&#xff0c;并且设置了id为app的div盒子。该页面即为Vue单页面应用的基础页面。后…

vue+node.js美食分享推荐管理系统 io551

&#xff0c;本系统采用了 MySQL数据库的架构&#xff0c;在开始这项工作前&#xff0c;首先要设计好要用到的数据库表。该系统的使用者有二类&#xff1a;管理员和用户&#xff0c;主要功能包括个人信息修改&#xff0c;用户、美食类型、美食信息、订单信息、美食分享、课程大…

汇编语言movs指令学习

字符串传送指令(Move String Instruction) movs 该指令是把指针DS:SI所指向的字节、字或双字传送给指针ES:DI所指向内存单元&#xff0c;并根据标志位DF对寄存器DI和SI作相应增减。该指令的执行不影响任何标志位。 记不清这指令是8086就有的&#xff0c;还是386以后新加的&…

大蟒蛇(Python)笔记(总结,摘要,概括)——第9章 类

目录 9.1 创建和使用类 9.1.1 创建Dog类 9.1.2 根据类创建实例 9.2 使用类和实例 9.2.1 Car类 9.2.2 给属性指定默认值 9.2.3 修改属性的值 9.3 继承 9.3.1 子类的_init_()方法 9.3.2 给子类定义属性和方法 9.3.3 重写父类中的方法 9.3.4 将实例用作属性 9.3.5 模拟实物 9.…

*MYSQL--索引--内部原理

MYSQL的索引根据功能,主要有三大类型: 1.HASH索引 2.二叉树 3.BTREE索引 一:HASH索引 1.内部原理: 在设置了某列为索引列之后,并且开始或者将要在相应索引列创建数据的时候,系统通过某种算法 F(X) 自动计算出来一个十六进制的哈希值,这个哈希值能够对应相应的字段值 所以…

香港Web3:香港虚拟货币 OTC 业务如何合规开展?

撰文&#xff1a;刘红林 文章来源Techub News专栏作者&#xff0c;搜Tehub News下载查看更多Web3资讯。 香港虚拟货币监管两手抓 2024 年 2 月 2 日&#xff0c;香港财经事务及库务局局长许正宇表示&#xff0c;政府认为有需要把虚拟货币场外交易所 (OTC) 纳入监管&#xff0…

software framwork

software framwork软件架构 软件架构&#xff0c;之前图没找到&#xff0c;随手画了一个啦&#xff0c;了解架构细分职能和工作任务&#xff1a; 下图&#xff0c;第一是客户端架构包项目&#xff0c;第二是服务端架构包项目 -----------------------------------------------…