2. 二叉树的中序遍历 (inOrder)

头文件: 用到的是前序遍历中的头文件,具体可参见 "二叉树的前序遍历"

一,中序遍历的概念

        中序遍历
                按照 左儿子-根节点-右儿子 的顺序访问二叉树

        方式
                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 遍历

/*
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

你可能感兴趣的:(必会的算法,c++,算法,数据结构)