原文地址: http://blog.csdn.net/fisher_jiang/archive/2008/06/01/2437714.aspx 原文的算法描述和代码注释看了半天才明白,因此我把代码注释按我自己的理解改了改: void InOrder2(Node *T) { if (T == NULL) { return; } // 算法关键: // 1. 如果走到了结点p的左/右子树,就把p的左/右子结点指针指向p的父结点,用于回溯。 // 2. 利用所有叶子结点的两个NULL指针,模拟一个栈(形式为链表+栈顶指针),该栈记录所有“双子树结点”(用于判断当前结点是父结点的左结点还是右结点)。 // 模拟方式为,left指针指向一个双子树结点(当成value域使用),right指针指向下一个双子树结点(当成next域使用)。 Node *top = NULL; // 栈顶指针。 Node *lr = NULL; // 当前双子树结点。 Node *p = T, *q = T; // 两个遍历指针,p为q的子结点。 while (true) { while (true) { if (p->pLeft_ == NULL && p->pRight_ == NULL) { // 访问到底,输出该结点。 p->Print(); break; } else if(p->pLeft_ == NULL) { // 沿左子树访问到底,转向访问右子树。 p->Print(); // // step A : 父结点 -> 右子结点,和step C互为逆操作。 Node *r = p->pRight_; p->pRight_ = q; q = p; p = r; // } else { // 一直沿左子树访问下去。 // // step B : 父结点 -> 左子结点,和step D互为逆操作。 Node *r = p->pLeft_; p->pLeft_ = q; q = p; p = r; // } } Node *leaf = p; // 记录当前叶结点。 while (true) { if (p == T) { // 如果回退到根,返回。 return; } else if (q->pLeft_ == NULL) { // 如果父结点q的pLeft_为空,表示p为q的右儿子。 // // step C : 右子结点 -> 父结点,和step A互为逆操作。 Node *r = q->pRight_; q->pRight_ = p; p = q; q = r; // } else if (q->pRight_ == NULL) { // 如果父结点q的pRight_为空,表示p为q的左儿子。 // // step D : 左子结点 -> 父结点,和step B互为逆操作。 Node *r = q->pLeft_; q->pLeft_ = p; p = q; q = r; // p->Print(); } else { // 如果p的父亲q有两个子树,用以下方法判断p是q的左儿子还是右儿子。 if (q == lr) { // 维护栈的意义就在于这个判断。 // q已经被压栈过,说明p是q的右儿子(q在其左子树走到叶结点时压栈)。 // // step i : 退栈,恢复叶结点的子结点指针为NULL,和step ii互为逆操作。 Node *r = top; lr = r->pLeft_; top = r->pRight_; r->pLeft_ = r->pRight_ = NULL; // // // step C : 右子结点 -> 父结点,和step A互为逆操作。 r = q->pRight_; q->pRight_ = p; p = q; q = r; // } else { // p是q的左儿子。 q->Print(); // // step ii : 当前双子树结点压栈,和step i互为逆操作。 leaf->pLeft_ = lr; leaf->pRight_ = top; top = leaf; lr = q; // // // step D : 左子结点 -> 父结点,和step B互为逆操作。 Node *r = q->pLeft_; q->pLeft_ = p; p = q->pRight_; q->pRight_ = r; // break; } } } } }