头文件: 用到的是前序遍历中的头文件,具体可参见 "二叉树的前序遍历"
中序遍历
按照 左儿子-根节点-右儿子 的顺序访问二叉树
方式
1,递归遍历
2,栈迭代遍历 (借助栈结构)
3,Morris 遍历 (栈迭代的基础上优化空间复杂度)
/*
递归遍历
先访问当前节点的左子树,然后再访问当前节点,最后访问当前节点的右子树
T : O(n),n 是二叉树的节点数,每个节点恰好被遍历1次
S : O(n), 递归过程中的栈开销,平均情况下为 O(log n),最坏情况下树为链状,为 O(n)
*/
void inTraverse(TreeNode* root, vector& vec) {
if (root) {
inTraverse(root->left, vec); // 访问当前节点的左子树
vec.emplace_back(root->val); // 访问当前节点
inTraverse(root->right, vec); // 访问当前节点的右子树
}
}
/*
栈迭代遍历
1,因为中序遍历是先访问左子树,所以我们应该一直将当前节点的左子树加入到栈中,直道当前节点没有左子树。
2,因为当前节点没有左子树,所以当前节点就相当于是一个没有左子树的根节点,从栈中弹出,访问当前节点。
3,然后当前节点指向右子树,继续步骤1。
时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
空间复杂度:O(n),空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。
*/
void inTraverseN(TreeNode* root, vector& vec) {
stack stack;
while (root || !stack.empty()) { // 只要栈未空,或当前节点不是空节点
while (root) { // 一直将当前节点的左子树加入到栈中,循环结束时,栈顶元素是没有左子树的
stack.emplace(root);
root = root->left;
}
root = (TreeNode*)stack.top(); // 弹出栈顶元素,此时栈顶节点就相当于是一个没有左子树的根节点
stack.pop();
vec.emplace_back(root->val); // 访问它
root = root->right; // 让当前节点指向右子树
}
}
/*
Morris 遍历
模版与前序遍历相同,只是要思考,在什么时候去访问该节点 ?
1,当前节点没有左子树时,当前节点就相当于没有左子树的根节点,此时需要访问它。
2,当前节点有左子树,但是左子树的最右节点已经指向了当前节点,说明当前节点的整个左子树已经遍历结束了,此时也需要访问当前节点。
T : O(n),其中 n 是二叉树的节点数。没有左子树的节点只被访问一次,有左子树的节点被访问两次。
S : O(1), 只操作已经存在的指针(树的空闲指针),因此只需要常数的额外空间。
*/
void inTraverseM(TreeNode* root, vector& vec) {
TreeNode* cur = root;
TreeNode* mostRight = nullptr;
while (cur) {
mostRight = cur->left;
if (mostRight) {
while (mostRight->right && mostRight->right != cur) {
mostRight = mostRight->right;
}
if (mostRight->right == nullptr) {
mostRight->right = cur;
cur = cur->left;
}
else{
vec.emplace_back(cur->val); // ## 当最右节点的右子树指向了 cur,说明整个左子树都遍历完了,此时 cur 就是根节点,访问它,然后遍历右子树
mostRight->right = nullptr;
cur = cur->right;
}
}
else {
vec.emplace_back(cur->val); // ## 当没有左子树的时候,当前节点就相当于一个没有左子树的根节点,需要访问它
cur = cur->right;
}
}
}
void inOrderTraversal(TreeNode* root) {
vector ret;
inTraverse(root, ret);
cout << "中序遍历, 递归 : " << endl;
PRINT(ret);
ret.clear();
inTraverseN(root, ret);
cout << "中序遍历, 栈迭代 : " << endl;
PRINT(ret);
ret.clear();
inTraverseM(root, ret);
cout << "中序遍历, Morris : " << endl;
PRINT(ret);
}
#if IN_ORDER_TRAVERSAL
int main() {
TreeNode* root = getRandomTree();
inOrderTraversal(root);
return 0;
}
#endif