前言
本文将主要围绕以下问题进行讨论:
二叉树的前序遍历。
本文将分别以两种方法【递归】【迭代】来解决这一问题(文章末尾附完整代码)
一、如何理解前序遍历?
二叉树的前序遍历是一种常见的遍历方式,主要按照 “根节点 - 左子树 - 右子树” 的顺序访问二叉树的每个节点。
如下:
1
/ \
2 3
/ \ / \
4 5 6 7
首先访问根节点 1
遍历左子树:
接着递归地对根节点的左子树进行前序遍历。
在上面的例子中,先访问根节点 1 后,接着对以节点 2 为根的左子树进行前序遍历。首先访问节点 2,然后访问节点 2 的左子节点 4,再访问节点 2 的右子节点 5。
遍历右子树:
当左子树遍历完成后,递归地对根节点的右子树进行前序遍历。
在例子中,左子树遍历完后,对以节点 3 为根的右子树进行前序遍历。首先访问节点 3,然后访问节点 3 的左子节点 6,再访问节点 3 的右子节点 7。
题目链接: [点击跳转] Leetcode 144. 二叉树的前序遍历
二、方法一(递归法)
递归法通过深度优先搜索,将二叉树的所有节点全部按照前序的方式遍历出来。
根据示例所给出的输出格式,我们需要定义一个vector类型的变量,并且在最后输出这个变量。同时,我们还应注意到,如果将空节点传入递归函数这种,应该返回一个空值。
代码如下:
vector<int> tre;
void preOrder(TreeNode *root) {
if (root == nullptr) {
return;
}
}
这个时候,我们就可以根据前序的“根-左-右”原则进行排序和递归啦!
vector<int> tre;
void preOrder(TreeNode *root) {
if (root == nullptr) {
return;
}
tre.push_back(root->val);
preOrder(root->left);
preOrder(root->right);
}
完整代码如下:
/**
* 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> tre;
void preOrder(TreeNode *root) {
if (root == nullptr) {
return;
}
tre.push_back(root->val);
preOrder(root->left);
preOrder(root->right);
}
vector<int> preorderTraversal(TreeNode *root) {
preOrder(root);
return tre;
}
};
时间复杂度为O(n)。
三、方法二(迭代法)
采用了迭代的方式实现前序遍历,借助栈来保存已经访问过的节点,以便在遍历完左子树后能够回到父节点并继续遍历右子树。
通过不断地将当前节点压入栈并更新为其左子节点,直到左子树为空,然后从栈中弹出节点并更新为其右子节点,实现了前序遍历的顺序。
如下代码可以实现前序遍历,并且已标注好每句的作用:
vector<int> tre;
stack<TreeNode*> sta;//建栈
vector<int> preorderTraversal(TreeNode* root) {
if (root == nullptr) {//对传入的根节点先做一个判断
return tre;
}
while (!sta.empty() || root != nullptr) {//栈不为空,或者节点不为空时,都将进入下面的循环
while (root != nullptr) {//当节点还没遍历到树的末端时
tre.push_back(root->val);//将节点值传入
sta.push(root);//将该节点压入栈中
root = root->left;//指向左子节点
}
root = sta.top();//取栈顶元素
sta.pop();//出栈
root = root->right;//指向右子节点
}
return tre;
}
在循环内部有一个嵌套的while循环,这个循环会一直执行直到当前节点root为nullptr。
在这个循环中,每次都会将当前节点的值加入向量tre中,然后将当前节点压入栈sta,并将当前节点更新为其左子节点,继续遍历左子树。以此实现先访问根节点,再遍历左子树的前序遍历的一部分。
当内部循环结束时,说明已经遍历到了左子树的最底层,此时从栈中弹出一个节点作为当前节点,并将当前节点更新为其右子节点,继续遍历右子树。如果栈为空且当前节点也为nullptr,说明整个二叉树已经遍历完毕。
完整代码如下:
/**
* 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> tre;
stack<TreeNode*> sta;//
vector<int> preorderTraversal(TreeNode* root) {
if (root == nullptr) {
return tre;
}
while (!sta.empty() || root != nullptr) {
while (root != nullptr) {
tre.push_back(root->val);
sta.push(root);
root = root->left;
}
root = sta.top();
sta.pop();
root = root->right;
}
return tre;
}
};
时间复杂度为O(n)。