二叉树遍历
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
PS: 中序遍历: 左子树->根节点->右子树; 顺序 访问 节点的值。
解法1: 递归, 有很多方式,这是其中一种。 时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( n ) O(n) O(n);
/**
* 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) {
// 再简单的题目, 不复习,也忘记,突然之间也不一定做的出来;
// 递归
if(root == nullptr)
return {};
vector<int> ans;
if(root != nullptr && root->left){
vector<int> left = inorderTraversal(root->left);
ans.insert(ans.end(), left.begin(), left.end());
}
ans.push_back(root->val); // 中序遍历,中间遍历根节点
if(root != nullptr && root->right){
vector<int> right = inorderTraversal(root->right);
ans.insert(ans.end(), right.begin(), right.end()); // 合并两个 vector
}
return ans;
}
};
方法2: 迭代; 迭代和递归都要掌握,因为这是基础; 用栈
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
// 迭代; 栈, 先进后出;
vector<int> ans;
stack<TreeNode*> stk; // 回溯
while(root != nullptr || !stk.empty()){
while(root!=nullptr){
stk.push(root);
root = root->left; // 一致遍历左子树,直到为null;
}
root = stk.top(); // 赋值给root;
stk.pop();
ans.push_back(root->val); // 中序遍历
root = root->right; // 遍历右子树;
}
return ans;
}
};
方法3: Morris(ˈmɒrɪs, 莫里斯)遍历, 时间复杂度
O
(
n
)
O(n)
O(n), 空间复杂度
O
(
1
)
O(1)
O(1), 需要重点记忆的方法;
Morris中序遍历方法的步骤:
-
如果root无左孩子,现将root的值加入ans, 然后访问root的右孩子,即 root=root->right;
-
如果root有左孩子,则找到root左子树上最右侧的节点,记为mostright(predecessor),根据mostright的右孩子是否为nullptr,进行如下操作:
- 如果mostright->right为nullptr, 则将mostright->right=root, 然后再访问root的左孩子,即root = root->left;
- 如果mostright->right不为nullptr, 则此时mostright->right指向root, 说明已经遍历完root的左子树,则将mostright->right=nullptr, 将root的值加入ans, 然后再访问root的右孩子,即root=root->right;
-
循环1,2;
中序遍历输出节点值的时机: 左子树为null,或者遍历完左子树;
/**
* 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) {
// Morris遍历, O(n), O(1)
vector<int> ans;
TreeNode* mostright; // 左子树的最右侧,又叫predecessor; 和root指向不同,用来记录
TreeNode* cur = root; // 避免root节点变化
while(cur != nullptr){
if(cur->left !=nullptr){
mostright = cur->left;
// 遍历左子树的最右侧节点, 不等于root,是为了往上走时使用;
while(mostright->right != nullptr && mostright->right != cur)
{
mostright = mostright->right;
}
if(mostright->right == nullptr){
mostright->right = cur; // 只当当前节点
cur = cur->left; // 遍历左子树
}else{
// 遍历完左子树了;
mostright->right = nullptr; // 断开链接
ans.push_back(cur->val); // 加入当前节点;
cur = cur->right; // 左子树已经遍历完毕,再遍历右子树,其实是返回 上层;
}
}else{
// 左子树为null; 记录当前节点,并指向右子树
ans.push_back(cur->val);
cur = cur->right; // 包含正常右子树,和往上返回
}
}
return ans;
}
};
给你二叉树的根节点 root
,返回它节点值的 前序
遍历。
解法1: 迭代, 36ms; 时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( n ) O(n) O(n);
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
// 迭代; 根节点->左子树->右子树
vector<int> ans;
stack<TreeNode*> stk;
while(root!=nullptr || !stk.empty()){
while(root){
stk.push(root);
ans.push_back(root->val); // 先遍历根节点
root = root->left;
}
root = stk.top(); stk.pop();
root = root->right;
}
return ans;
}
};
解法2: 递归, 0ms;
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
// 递归; 根节点->左子树->右子树
if(root == nullptr)
return {};
vector<int> ans;
ans.push_back(root->val);
if(root->left)
{
vector<int> tmp = preorderTraversal(root->left);
ans.insert(ans.end(), tmp.begin(), tmp.end());
}
if(root->right){
vector<int> tmp = preorderTraversal(root->right);
ans.insert(ans.end(), tmp.begin(), tmp.end());
}
return ans;
}
};
解法3: Morris前序遍历
和Morris中序遍历的区别,在于处理根节点值的时机:
Morris中序遍历处理节点值时机为:
- 左子树为nullptr时,将根节点值加入数组;
- 左子树不为nullptr时,遍历完左子树时,将根节点值加入数组;
Morris前序遍历处理节点值时机为:
- 左子树为nullptr时,将根节点值加入数组;
- 左子树不为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> preorderTraversal(TreeNode* root) {
// Morris前序遍历,和中序的区别,在于 处理 节点值的时机;
vector<int> ans;
TreeNode* mostright;
TreeNode* cur = root; // 指向root,避免直接使用root,导致根节点丢失
while(cur != nullptr){
if(cur->left != nullptr){
mostright = cur->left;
while(mostright->right != nullptr && mostright->right != cur){
mostright = mostright->right;
}
if(mostright->right == nullptr){
// 和前序的区别;
ans.push_back(cur->val); // 前序遍历,此处加入值, 遍历左子树之前,遍历根节点
mostright->right = cur;
cur = cur->left;
}else{
// 左子树遍历完了, 去遍历右子树即可,
mostright->right = nullptr;
cur = cur->right;
}
}else{
// 左子树为null; 遍历右子树
ans.push_back(cur->val);
cur = cur->right;
}
}
return ans;
}
};
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
解法1: 递归, 时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( n ) O(n) O(n);
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
// 后序: 先左子树 -> 右子树 -> 根节点
if(root == nullptr)
return {};
vector<int> res;
if(root->left)
{
vector<int> tmp = postorderTraversal(root->left);
res.insert(res.end(), tmp.begin(), tmp.end());
}
if(root->right){
vector<int> tmp = postorderTraversal(root->right);
res.insert(res.end(), tmp.begin(), tmp.end());
}
res.push_back(root->val);
return res;
}
};
解法2: 迭代, 后序遍历迭代不好写,反复记忆思路吧;
/**
* 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> ans;
TreeNode* prev; // 用来避免重复遍历有右子树
stack<TreeNode*> stk;
while(root != nullptr || !stk.empty())
{
while(root){
stk.push(root);
root = root->left;
}
root = stk.top(); stk.pop();
if(root->right ==nullptr || root->right == prev)
{ // 右子树为nullptr, 或者 被 遍历过了
ans.push_back(root->val);
prev = root;
root = nullptr; // 这样 回溯
}else{ // 先遍历右子树
stk.push(root);
root = root->right;
}
}
return ans;
}
};
解法3: Morris遍历
Morris后续遍历, 处理节点值的时机为:
-
当前节点的左子树遍历完毕时,对当前节点的左子节点到左子树的最右侧节点,这条路径上的节点值 倒序 加入数组;
-
最后,将root节点到 右子树最右侧节点的 路径上值 倒序加入数组;
/**
* 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:
void addPath(vector<int> & vec, TreeNode * node){
int count = 0;
while(node != nullptr){
++count;
vec.emplace_back(node->val);
node = node->right; // 一直往右走,是因为左子树已经处理完
}
reverse(vec.end()-count, vec.end()); // 反转
}
vector<int> postorderTraversal(TreeNode* root) {
// Morris 后续遍历; 什么时候获取根节点的值?
vector<int> ans;
TreeNode * mostright;
TreeNode * cur = root;
while(cur != nullptr){
if(cur->left != nullptr){
mostright = cur->left;
while(mostright->right != nullptr && mostright->right != cur){
mostright = mostright->right;
}
if(mostright->right == nullptr){
mostright->right = cur;
cur = cur->left; // 遍历左子树
}else{
// 左子树遍历完毕, 遍历右子树;
mostright->right = nullptr;
// 把左子树节点的值倒序加入数组;
addPath(ans, cur->left);
cur = cur->right;
}
}else{
// 左子树为null, 遍历右子树;
cur = cur->right;
}
}
// Morris后续遍历,整体流程变化不大,只是 处理节点值的方式变了;
addPath(ans, root); // 这是因为最后root右子树的 最右侧 没有记录,所以加上;
return ans;
}
};