常见的二叉树非递归算法都是用栈保存访问路径上的结点,这样使空间复杂为o(n),其中n为树最大深度。空间复杂度为o(1)的算法并没有以牺牲时间复杂度为代价,它只是巧妙的运用叶子结点左右孩子指针为空这一事实,将所有的叶子组成一链栈用于保存回退信息,其中叶子结点的lchild域相当于链表的data域,rchild相当于链表的next域,是一种“废物利用”的思想,本质上还是用了栈,只是没用分配栈空间而已。
事实上,像二叉树这种递归定义的非线性结构,在设计相应的非递归程序时最难考虑的是从当前结点向上回退的情况,向下走的情况很简单:先向左走,左面走不下去了就向右。回退的时候有两个要点:
- 找到其父结点的指针以便回退。
- 判断出它是父结点的左孩子还是右孩子。常见的用栈保存信息的算法,栈中保存了从根结点到当前结点的路径信息,当然可以正确的回退。
该算法的大概思想是:p作为当前结点,q作为它的父结点(满足要点1),如此深入下去,如果p是q的左孩子,就把q的左指针指向q的父亲,这样q作指针所指向的结点就不在是p了,而是p的爷爷结点了,这样的做法是为了在回退时找到父结点。
为了满足要点2:判断回退时它是父结点的左孩子还是右孩子,有三种情况要考虑:
- 回退时如果q的lchild为空,表示q只有右子树p,这样就判断出来了p只可能是q的右孩子,剩下的问题就是回退和恢复q的rchild了。
- 如果q的rchild为空,这个情况和1相似......
- 如果q的lchild和rchild都不为空,这样回退时候就比较难判断p到底是q的左还是右儿子了?
该算法的解决方法为:用av保存当前叶子结点以作为为栈分配的元素,这个栈用于记录遇到的双子树结点。
具体实现代码如下:
1 void InOrder2(pstuBTnode pstuParentnode)
2 {
3 pstuBTnode top = NULL; //top为栈顶指针
4 pstuBTnode pstuChildNode = NULL; //pstuChildNode记录当前的右子树结点
5 pstuBTnode pstuTempNode = NULL; //pstuTempNode为临时变量
6 pstuBTnode pstuLeafNode = NULL; //pstuLeafNode为当前可用叶子结点,
7 pstuBTnode pstuCurrentNode = pstuParentnode; //pstuCurrentNode为当前访问结点
8 pstuBTnode pstuCurrentParentNode = pstuParentnode; //pstuCurrentParentNode为pstuCurrentNode的父结点
9
10 if(pstuParentnode == NULL)
11 return;
12
13 //遍历所有节点
14 while(TRUE)
15 {
16 //向下访问搜索 遍历子树,找出叶子节点
17 while(TRUE)
18 {
19 if((pstuCurrentNode->lchild == NULL)&&(pstuCurrentNode->rchild == NULL)) //如果为叶子节点,就输出该结点
20 {
21 VisitNode(pstuCurrentNode);
22 break;
23 }
24 else if(pstuCurrentNode->lchild == NULL) //沿左子树访问到底,转向访问右子树
25 {
26 VisitNode(pstuCurrentNode);
27 pstuTempNode = pstuCurrentNode->rchild; //pstuTempNode保存右子树
28 pstuCurrentNode->rchild = pstuCurrentParentNode; //右子树保存父节点
29 pstuCurrentParentNode = pstuCurrentNode;
30 pstuCurrentNode = pstuTempNode; //pstuCurrentNode为右子树
31 }
32 else //一直沿左子树访问下去
33 {
34 pstuTempNode = pstuCurrentNode->lchild; //保存左子树
35 pstuCurrentNode->lchild = pstuCurrentParentNode; //左子树保存父节点
36 pstuCurrentParentNode = pstuCurrentNode; //pstuCurrentParentNode为pstuCurrentNode的父节点
37 pstuCurrentNode = pstuTempNode; //pstuCurrentNode为左子树
38 }
39 }
40
41 pstuLeafNode = pstuCurrentNode; //pstuCurrentNode为叶子结点,pstuLeafNode记录当前叶子结点
42
43 //回溯父节点
44 while(TRUE)
45 {
46 if(pstuCurrentNode == pstuParentnode)
47 {//如果回退到根,返回
48 return;
49 }
50 else if(pstuCurrentParentNode->lchild == NULL)
51 {//如果父结点pstuCurrentParentNode的lchild为空,表示pstuCurrentNode为pstuCurrentParentNode的右子节点
52 pstuTempNode = pstuCurrentParentNode->rchild; //向父结点的右儿子方向回退
53 pstuCurrentParentNode->rchild = pstuCurrentNode; //重新连接父指针
54 pstuCurrentNode = pstuCurrentParentNode;
55 pstuCurrentParentNode = pstuTempNode;
56 }
57 else if(pstuCurrentParentNode->rchild == NULL) //类似上面
58 {
59 pstuTempNode = pstuCurrentParentNode->lchild;
60 pstuCurrentParentNode->lchild = pstuCurrentNode;
61 pstuCurrentNode = pstuCurrentParentNode;
62 pstuCurrentParentNode = pstuTempNode;
63 VisitNode(pstuCurrentNode);
64 }
65 else//如果当前节点pstuCurrentNode的父节点pstuCurrentParentNode有两个子树,则判断当前节点是的左还是右子节点
66 {
67 if(pstuCurrentParentNode == pstuChildNode) //pstuCurrentNode是pstuCurrentParentNode的右子节点
68 {
69 pstuTempNode = top; //退栈
70 pstuChildNode = pstuTempNode->lchild; //lchild相当于链表的data域,rchild相当next域
71 top = pstuTempNode->rchild;
72 pstuTempNode->lchild = pstuTempNode->rchild = NULL;//退栈结束
73 pstuTempNode = pstuCurrentParentNode->rchild;
74 pstuCurrentParentNode->rchild = pstuCurrentNode;
75 pstuCurrentNode = pstuCurrentParentNode;
76 pstuCurrentParentNode = pstuTempNode;
77 }
78 else //pstuCurrentNode是左子节点
79 {
80 VisitNode(pstuCurrentParentNode);
81 pstuLeafNode->lchild = pstuChildNode; //入栈加回退操作
82 pstuLeafNode->rchild = top;
83 top = pstuLeafNode;
84 pstuChildNode = pstuCurrentParentNode;
85 pstuTempNode = pstuCurrentParentNode->lchild;
86 pstuCurrentParentNode->lchild = pstuCurrentNode;
87 pstuCurrentNode = pstuCurrentParentNode->rchild;
88 pstuCurrentParentNode->rchild = pstuTempNode;
89 break;
90 }
91 }
92 }
93 }
94 }