二叉树的数据结构如下:
typedef struct TreeNode
{
int val;
TreeNode* left; //左孩子
TreeNode* right; //右孩子
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
对于前序遍历,遍历访问节点处理规则如下:优先访问根结点,然后再分别访问左孩子和右孩子。
即对任一节点P,将其看作根节点,先访问节点P的值,再将其入栈;随后按同样规则访问其左子树,左子树访问完成后再访问右子树。
具体处理:
步骤1.访问结点P,并将结点P入栈
步骤2.判断结点P的左子节点是否为空,若为空,则取栈顶节点并进行出栈操作,并将栈顶结点的右子节点置为当前的结点P,循环至步骤1;若不为空,则将P的左子节点置为当前的结点P,再循环步骤2
步骤3.直到P为NULL并且栈为空,则遍历结束
void preorderTraversal(TreeNode *root) {
if (root == NULL) return;
stack<TreeNode *> s;
TreeNode *p = root;
while (p || !s.empty()) {
while (p) {
s.push(p);
cout << p->val;
p = p->left;
}
if (!s.empty()) {
p = s.top();
s.pop();
p = p->right;
}
}
return;
}
对于中序遍历,遍历访问节点处理规则如下:优先访问左子节点,然后再分别访问根节点和右子节点。
具体处理:
对任一节点P
步骤1.若P左子节点不为空,则将P入栈并将P的左子节点置为当前的P,并返回步骤1;若P的左子节点为NULL,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右子节点,并返回步骤1
步骤2.直到P为NULL并且栈为空,则遍历结束
void inorderTraversal(TreeNode* root) {
if (!root) return;
TreeNode *p = root;
stack<TreeNode *> s;
while (p || !s.empty()) {
while (p) {
s.push(p);
p = p->left;
}
if (!s.empty()) {
p = s.top();
cout << p->val;
s.pop();
if (p->right) {
p = p->right;
}
else {
p = NULL;
}
}
}
return;
}
对于后序遍历,遍历访问节点处理规则如下:优先访问左子节点,然后再访问右子节点,最后访问根节点。
具体处理:
思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左子节点的结点,此时该结点(没有左子节点的结点)出现在栈顶,但是此时不能将其出栈并访问, 因此其右子节点还为被访问,一个根节点被访问的前提是:无右子树或右子树已被访问过。
所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右子节点时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就 保证了正确的访问顺序。
因此,考虑记录当前节点以及上次访问的节点的位置,当 (1)上次访问节点是当前节点的右子节点 或者 (2)当前节点无右子节点并且上次访问节点是当前节点的左子节点 时,即可访问当前节点
void postorderTraversal(TreeNode* root) {
if (!root) return;
stack<TreeNode *> s;
TreeNode *pCurrent, *pLastVisit;
pCurrent = root;
while (pCurrent || !s.empty()) {
while (pCurrent) {
s.push(pCurrent);
pCurrent = pCurrent->left;
}
if (!s.empty()) {
pCurrent = s.top();
s.pop();
if (!pCurrent->right || pCurrent->right == pLastVisit) {
cout << pCurrent->val;
pLastVisit = pCurrent;
pCurrent = NULL;
}
else {
s.push(pCurrent);
pCurrent = pCurrent->right;
}
}
}
return;
}